copying: fold in graph copying (shared nodes, cycles, identity-map fix), serialization rules (constructors, record canonical constructor, ObjectInputFilter), record array components, clone() as a defensive copy, two more compile-error exhibits; 24 tests

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017ic4b4BL3cEPHQTk2McE7C
This commit is contained in:
2026-11-12 20:29:58 +05:30
co-authored by Claude Sonnet 5.5
parent 38fbd513c9
commit c8588ae520
17 changed files with 442 additions and 11 deletions
+3 -2
View File
@@ -1,3 +1,4 @@
[INFO] Tests run: 17, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.145 s -- in com.ankurm.copying.CopyingTest
[INFO] Tests run: 17, Failures: 0, Errors: 0, Skipped: 0
[INFO] Tests run: 17, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.150 s -- in com.ankurm.copying.CopyingTest
[INFO] Tests run: 7, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.053 s -- in com.ankurm.copying.CopyingExtrasTest
[INFO] Tests run: 24, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS
+11
View File
@@ -0,0 +1,11 @@
== 1. Diamond: root.left and root.right are the same node ==
original : left == right : true
copy constructor : left == right : false
identity-map copy : left == right : true
serialization copy : left == right : true
== 2. Cycle: a -> b -> a ==
original : a.left.left == a : true
copy constructor : StackOverflowError
identity-map copy : copy.left.left == copy : true
identity-map copy : copy != original : true
+11
View File
@@ -0,0 +1,11 @@
== 1. Classes: only the first non-serializable superclass constructor runs ==
after new Child() : Child runs = 1, Parent runs = 1
after the copy : Child runs = 1, Parent runs = 2
== 2. Records: the canonical constructor DOES run ==
after new Money(..) : canonical runs = 1
after the copy : canonical runs = 2
== 3. An allow-list filter on the reading side ==
list of String and Money : [text, Money[paise=1]]
list holding an AtomicInteger : InvalidClassException: filter status: REJECTED
+9
View File
@@ -0,0 +1,9 @@
== 1. RawPacket keeps the caller's array and returns its own ==
after raw[0] = 99 : [99, 2, 3]
after rp.data()[1] = 88 : [99, 88, 3]
== 2. Packet copies in the constructor and in the accessor ==
after the same two writes : [1, 2, 3]
== 3. The generated equals() still compares the arrays by identity ==
new Packet({1}).equals(new Packet({1})) : false
+7
View File
@@ -0,0 +1,7 @@
== 1. A 'defensive' copy made with clone() trusts the argument's own clone() ==
clone() result is the attacker's object : true
clone() result class : EvilDate
== 2. A copy made with the constructor does not ==
new Date(getTime()) is the attacker's object : false
new Date(getTime()) result class : Date
+5
View File
@@ -0,0 +1,5 @@
$ javac src/broken/ForgotToOverride.java
src/broken/ForgotToOverride.java:10: error: clone() has protected access in Object
Object copy = foo.clone();
^
1 error
+11
View File
@@ -0,0 +1,11 @@
$ javac src/broken/WithExpression.java
src/broken/WithExpression.java:8: error: ';' expected
Point q = p with { x = 10; };
^
src/broken/WithExpression.java:8: error: not a statement
Point q = p with { x = 10; };
^
src/broken/WithExpression.java:8: error: ';' expected
Point q = p with { x = 10; };
^
3 errors