-
Notifications
You must be signed in to change notification settings - Fork 274
Synthesise lambda classes #4787
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
LAJW
merged 33 commits into
diffblue:develop
from
smowton:smowton/feature/synthesise-lambda-classes
Aug 8, 2019
Merged
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
da9ff09
Factor out create_stub_method_symbol
smowton 2ad7ccf
Record class' instance methods against their type
smowton 31caca0
Implement lambda code synthesis
smowton 3e1d7ca
Move lambda test script to test_goto_functions.desc
smowton 631b154
Remove no-longer-applicable explanation of symex-driven-loading-xfail
smowton cff0543
Lambda tests: use local interface
smowton a79fa31
Expand existing lambda test to check that lambdas behave as expected
smowton 06e9865
Ignore lambdas whose target interfaces have multiple methods
smowton db70af6
Add tests for as-yet unsupported lambda types
smowton 963a237
Add more lambda tests
b0475d0
Group create_invokedynamic_synthetic_classes into scopes
1c631fa
Expose only name/id instead of the class
3f157f6
Convert named scopes into functions
510d724
Convert lambdas into functions
9c466c7
Pull out function create_implemented_method_symbol for invokedynamic …
1e0165b
Combine create_synthetic_symbol and create_synthetic_type
04b6702
Decrease nesting and spacing name functions consistently
d141c77
Move the comment outside of the function
8be6241
Remove controversial get_ prefix
0ca2455
Replace includes with forward declarations
9c69d53
Get rid of the block scope and cmb variable
314cd7c
Use can_cast instead of id comparison
1536103
Avoid misleading special characters within lambda synthetic class names
smowton 2a0025d
Slice type_symbolt on creation
832f5a1
Rename implemneted_interface_tag to functional_interface_tag
d42aa87
Use `symbol.name` as the id
652da04
Rename method names in the lambda test
86554a9
Add reference equality and static initialized lambda check
eb0f4cc
Add nondet capture test
bd8f557
Add nondet array primitive and reference tests
c1a2f4d
Formatting
5c2717a
Fix invokedynamic typing
smowton a316fd5
Sync test_goto_functions and test.desc classpaths
smowton File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Binary file added
BIN
+189 Bytes
jbmc/regression/jbmc/lambda-unhandled-types/InterfaceDeclaringEquals.class
Binary file not shown.
6 changes: 6 additions & 0 deletions
6
jbmc/regression/jbmc/lambda-unhandled-types/InterfaceDeclaringEquals.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
interface InterfaceDeclaringEquals { | ||
|
||
public int f(int x); | ||
|
||
public boolean equals(Object other); | ||
} |
Binary file added
BIN
+289 Bytes
jbmc/regression/jbmc/lambda-unhandled-types/InterfaceWithDefaults.class
Binary file not shown.
6 changes: 6 additions & 0 deletions
6
jbmc/regression/jbmc/lambda-unhandled-types/InterfaceWithDefaults.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
interface InterfaceWithDefaults { | ||
|
||
public int f(int x); | ||
|
||
public default int g() { return 1; } | ||
} |
4 changes: 4 additions & 0 deletions
4
jbmc/regression/jbmc/lambda-unhandled-types/StubInterface.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
interface StubInterface { | ||
|
||
public int f(int x); | ||
} |
Binary file added
BIN
+138 Bytes
jbmc/regression/jbmc/lambda-unhandled-types/StubSuperinterface.class
Binary file not shown.
1 change: 1 addition & 0 deletions
1
jbmc/regression/jbmc/lambda-unhandled-types/StubSuperinterface.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
interface StubSuperinterface extends StubInterface {} |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
public class Test { | ||
|
||
public static void main() { | ||
|
||
StubInterface stubInterface = (x -> x); | ||
StubSuperinterface stubSuperinterface = (x -> x); | ||
InterfaceDeclaringEquals interfaceDeclaringEquals = (x -> x); | ||
InterfaceWithDefaults interfaceWithDefaults = (x -> x); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
CORE | ||
Test.class | ||
--function Test.main --verbosity 10 | ||
^VERIFICATION SUCCESSFUL$ | ||
^EXIT=0$ | ||
^SIGNAL=0$ | ||
^ignoring invokedynamic at java::Test.main:\(\)V address 0 which produces a stub type java::StubInterface | ||
^ignoring invokedynamic at java::Test.main:\(\)V address 6 which produces type java::StubSuperinterface which should have exactly one abstract method but actually has 0. | ||
^ignoring invokedynamic at java::Test.main:\(\)V address 12 which produces type java::InterfaceDeclaringEquals which should have exactly one abstract method but actually has 2. | ||
^ignoring invokedynamic at java::Test.main:\(\)V address 18 which produces type java::InterfaceWithDefaults which should have exactly one abstract method but actually has 2. | ||
-- | ||
-- | ||
This exercises four cases that aren't currently supported: stub interfaces, stub | ||
superinterfaces, interfaces that declare methods also declared on | ||
java.lang.Object, and interfaces that provide default methods. All we require at | ||
this point is that jbmc shouldn't crash when these are seen. |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,7 @@ | ||
public class B { | ||
public int y; | ||
public int y; | ||
|
||
public static java.util.function.Function<Double, Double> dmul = x -> x * 3.1415; | ||
public static Function<Double, Double> dmul = x -> x * 1.5; | ||
|
||
public void set(int x) { | ||
y = x; | ||
} | ||
public void set(int x) { y = x; } | ||
} |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
|
||
interface BiFunction<From1, From2, To> { | ||
|
||
public To apply(From1 f1, From2 f2); | ||
} |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
@FunctionalInterface | ||
interface CustomLambda<T> { | ||
boolean is_ok(T t); | ||
boolean is_ok(T t); | ||
} |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
|
||
interface Function<From, To> { | ||
|
||
public To apply(From f); | ||
} |
Binary file not shown.
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,97 +1,185 @@ | ||
import java.util.function.*; | ||
|
||
public class Lambdatest { | ||
|
||
class A { | ||
int x; | ||
} | ||
|
||
CustomLambda<Integer> custom = x -> true; | ||
BiFunction<Float, Integer, Integer> add = (x0, y0) -> x0.intValue() + y0; | ||
int z = 10; | ||
|
||
A a; | ||
B b = new B(); | ||
|
||
public Integer g(Float x, Integer y, BiFunction<Float, Integer, Integer> fun) { | ||
return fun.apply(x, y); | ||
} | ||
|
||
public int f(Float x, Integer y, Integer z) { | ||
Integer tmp = add.apply(x, y); | ||
Function<Integer, Integer> mul = (a) -> a * tmp; | ||
return mul.apply(z); | ||
} | ||
|
||
public int i(int x) { | ||
int z = 5; | ||
Function<Integer, Integer> foo = (a) -> a * z; | ||
return foo.apply(x); | ||
} | ||
|
||
public int j(int x) { | ||
Function<Integer, Integer> foo = (a) -> a * z; | ||
return foo.apply(x); | ||
} | ||
|
||
public int k(int x) { | ||
a.x = 10; | ||
|
||
Function<Integer, Integer> foo = (y) -> y * a.x; | ||
return foo.apply(x); | ||
} | ||
|
||
public int l(int x) { | ||
b.y = 10; | ||
Function<Integer, Integer> foo = (y) -> { | ||
int r = y * b.y; | ||
b.set(r); | ||
return r; | ||
}; | ||
b = new B(); | ||
b.y = 14; | ||
return foo.apply(x); | ||
} | ||
|
||
public int m(int x) { | ||
b.y = 10; | ||
Function<Integer, Integer> foo = (y) -> { | ||
int r = y * b.y; | ||
b.y = r; | ||
return r; | ||
}; | ||
return foo.apply(x); | ||
} | ||
|
||
// test static field of different class | ||
public double d(Double x) { | ||
return B.dmul.apply(x); | ||
class A { | ||
int x; | ||
} | ||
|
||
CustomLambda<Integer> custom = x -> true; | ||
BiFunction<Float, Integer, Integer> add = (x0, y0) -> x0.intValue() + y0; | ||
int z = 10; | ||
|
||
A a = new A(); | ||
B b = new B(); | ||
|
||
public Integer captureNone(Float x, Integer y, | ||
BiFunction<Float, Integer, Integer> fun) { | ||
return fun.apply(x, y); | ||
} | ||
|
||
public int captureReference(Float x, Integer y, Integer z) { | ||
Integer tmp = add.apply(x, y); | ||
Function<Integer, Integer> mul = (a) -> a * tmp; | ||
return mul.apply(z); | ||
} | ||
|
||
public int captureInt(int x) { | ||
int z = 5; | ||
Function<Integer, Integer> foo = (a) -> a * z; | ||
return foo.apply(x); | ||
} | ||
|
||
public int captureThisPrimitive(int x) { | ||
Function<Integer, Integer> foo = (a) -> a * z; | ||
return foo.apply(x); | ||
} | ||
|
||
public int captureThisReference(int x) { | ||
a.x = 10; | ||
|
||
Function<Integer, Integer> foo = (y) -> y * a.x; | ||
return foo.apply(x); | ||
} | ||
|
||
public int captureAndCall(int x) { | ||
b.y = 10; | ||
Function<Integer, Integer> foo = (y) -> { | ||
int r = y * b.y; | ||
b.set(r); | ||
return r; | ||
}; | ||
b = new B(); | ||
b.y = 14; | ||
return foo.apply(x); | ||
} | ||
|
||
public int captureAndAssign(int x) { | ||
b.y = 10; | ||
Function<Integer, Integer> foo = (y) -> { | ||
int r = y * b.y; | ||
b.y = r; | ||
return r; | ||
}; | ||
return foo.apply(x); | ||
} | ||
|
||
// test static field of different class | ||
public double callStatic(Double x) { return B.dmul.apply(x); } | ||
|
||
public int capture2(Float a) { return add.apply(a, 1); } | ||
|
||
public boolean custom(Integer i) { return custom.is_ok(i); } | ||
|
||
public static void main(String[] args, int unknown) { | ||
Lambdatest lt = new Lambdatest(); | ||
if (unknown == 0) { | ||
// should succeed | ||
assert lt.captureNone(1.0f, 2, (x, y) -> x.intValue() + y) == 3; | ||
} else if (unknown == 1) { | ||
// should fail | ||
assert lt.captureNone(1.0f, 2, (x, y) -> x.intValue() + y) == 4; | ||
} else if (unknown == 2) { | ||
assert lt.captureReference(1.0f, 2, 3) == 9; // should succeed | ||
} else if (unknown == 3) { | ||
assert lt.captureReference(1.0f, 2, 3) == 10; // should fail | ||
} else if (unknown == 4) { | ||
assert lt.captureInt(2) == 10; // should succeed | ||
} else if (unknown == 5) { | ||
assert lt.captureInt(2) == 11; // should fail | ||
} else if (unknown == 6) { | ||
assert lt.captureThisPrimitive(3) == 30; // should succeed | ||
} else if (unknown == 7) { | ||
assert lt.captureThisPrimitive(3) == 31; // should fail | ||
} else if (unknown == 8) { | ||
assert lt.captureThisReference(4) == 40; // should succeed | ||
} else if (unknown == 9) { | ||
assert lt.captureThisReference(4) == 41; // should fail | ||
} else if (unknown == 10) { | ||
assert lt.captureAndCall(5) == 70; // should succeed | ||
assert lt.b.y == 70; // check side-effects of l method | ||
} else if (unknown == 11) { | ||
assert lt.captureAndCall(5) == 51; // should fail | ||
} else if (unknown == 12) { | ||
assert lt.captureAndAssign(6) == 60; // should succeed | ||
assert lt.b.y == 60; // check side-effects of m method | ||
} else if (unknown == 13) { | ||
assert lt.captureAndAssign(6) == 61; // should fail | ||
} else if (unknown == 14) { | ||
assert lt.callStatic(7.0) == 10.5; // should succeed | ||
} else if (unknown == 15) { | ||
assert lt.callStatic(7.0) == 12; // should fail | ||
} else if (unknown == 16) { | ||
assert lt.capture2(8.0f) == 9; // should succeed | ||
} else if (unknown == 17) { | ||
assert lt.capture2(8.0f) == 10; // should fail | ||
} else if (unknown == 18) { | ||
assert lt.custom(9); // should succeed | ||
} else if (unknown == 19) { | ||
assert !lt.custom(9); // should fail | ||
// Test array capture | ||
} else if (unknown == 20) { | ||
int array[] = new int[3]; | ||
Function<Integer, Integer> func = (x) -> x + array.length; | ||
assert func.apply(2) == 5; // should succeed | ||
} else if (unknown == 21) { | ||
int array[] = new int[3]; | ||
Function<Integer, Integer> func = (x) -> x + array.length; | ||
assert func.apply(2) == 6; // should fail | ||
// Test reference array | ||
} else if (unknown == 22) { | ||
Integer array[] = new Integer[3]; | ||
Function<Integer, Integer> func = (x) -> x + array.length; | ||
assert func.apply(2) == 5; // should succeed | ||
} else if (unknown == 23) { | ||
Integer array[] = new Integer[3]; | ||
Function<Integer, Integer> func = (x) -> x + array.length; | ||
assert func.apply(2) == 6; // should fail | ||
// Test outer class capture | ||
} else if (unknown == 24) { | ||
Outer outer = new Outer(); | ||
outer.value = 42; | ||
Outer.Inner inner = outer.new Inner(); | ||
Function<Integer, Integer> getter = inner.getOuterGetter(); | ||
assert (getter.apply(0) == 42); // should succeed | ||
} else if (unknown == 25) { | ||
Outer outer = new Outer(); | ||
outer.value = 42; | ||
Outer.Inner inner = outer.new Inner(); | ||
Function<Integer, Integer> getter = inner.getOuterGetter(); | ||
assert (getter.apply(0) != 42); // should fail | ||
// Static intialized lambda | ||
} else if (unknown == 26) { | ||
assert StaticLambdas.id.apply(96) == 96; // should succeed | ||
} else if (unknown == 27) { | ||
assert StaticLambdas.id.apply(96) != 96; // should fail | ||
// Reference equality of the captured object | ||
} else if (unknown == 28) { | ||
Object object = new Object(); | ||
Function<Integer, Object> lambda = (x) -> object; | ||
assert (lambda.apply(0) == object); // should succeed | ||
} else if (unknown == 29) { | ||
Object object = new Object(); | ||
Function<Integer, Object> lambda = (x) -> object; | ||
assert (lambda.apply(0) != object); // should fail | ||
} | ||
} | ||
|
||
public int capture2(Float a) { | ||
return add.apply(a, 1); | ||
} | ||
static void lambdaCaptureNondet(Integer value) { | ||
Function<Integer, Integer> func = (x) -> value; | ||
assert func.apply(0) == 42; | ||
} | ||
} | ||
|
||
public boolean custom(Integer i) { | ||
return custom.is_ok(i); | ||
} | ||
class StaticLambdas { | ||
final static Function<Integer, Integer> id = x -> x; | ||
} | ||
|
||
public static void main(String[] args) { | ||
// Uses all of the above test functions, to ensure they are loaded under | ||
// symex-driven loading: | ||
|
||
Lambdatest lt = new Lambdatest(); | ||
lt.f(0.0f, 0, 0); | ||
lt.i(0); | ||
lt.j(0); | ||
lt.k(0); | ||
lt.l(0); | ||
lt.m(0); | ||
} | ||
class Outer { | ||
int value; | ||
class Inner { | ||
Function<Integer, Integer> getOuterGetter() { return (x) -> value; } | ||
} | ||
} | ||
|
||
class C implements CustomLambda<Integer> { | ||
public boolean is_ok(Integer i) { | ||
return true; | ||
} | ||
public boolean is_ok(Integer i) { return true; } | ||
} |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
public class Nondet { | ||
int x; | ||
int y; | ||
public static void test(Nondet nondet) { | ||
Function<Integer, Integer> lambda = (z) -> nondet.x + nondet.y + z; | ||
assert (lambda.apply(0) == 42); | ||
} | ||
public static void testPrimitiveArray(int[] ints) { | ||
Function<Integer, Integer> lambda = (index) -> ints[index]; | ||
assert (lambda.apply(0) == 42); | ||
} | ||
public static void testReferenceArray(Nondet[] nondets) { | ||
Function<Integer, Integer> lambda = (index) -> nondets[index].x; | ||
assert (lambda.apply(0) == 42); | ||
} | ||
} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
CORE | ||
Nondet.class | ||
--function Nondet.test --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` | ||
line 6 assertion.*FAILURE$ | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
-- | ||
-- | ||
Check that objects created by the object factory can be captured by a lambda |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
CORE | ||
Nondet.class | ||
--function Nondet.testPrimitiveArray --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` | ||
line 10 assertion.*FAILURE$ | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
-- | ||
-- | ||
Check that primitive arrays created by the object factory can be | ||
captured by a lambda |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.