Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions docs/language_status/register/X39.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ reached: "no (unit-proven on CicsTask, the model-port DTO case included, and the
- **Model ports (#4956).** A model-port program is LINKed with its caller's DTO object only, no bytes, so `CicsTask.link` marks
the callee `linkedDto` and `xctl` refuses, by the same name and message, a successful XCTL whose COMMAREA is that DTO or an
object it holds by reference (a nested group, a table or its element, a list element, a map value, the byte area). A copy
(a new object), a failed XCTL, level 1 and the XCTL target's own DTO are not refused. Not detectable, so not refused: a
String or number field passed as the COMMAREA (a value, the same as a copy).
(a new object), a failed XCTL, level 1 and the XCTL target's own DTO are not refused. A String or number cannot be traced by
reference (#5060), so a successful XCTL whose COMMAREA equals a value the DTO holds (any field, nested or primitive) is refused
too; an equal-valued copy is over-refused (refuse, never guess), a value the DTO does not hold is not.
- **Aliases (#4957).** The det port marks an XCTL whose COMMAREA lies inside any LINKAGE 01 record, not only DFHCOMMAREA: a CALLed
subprogram's USING record (`CALL 'SUB' USING DFHCOMMAREA`) is the LINK caller's storage under another name, and refuses by the
same runtime check (only when the program was LINKed with a COMMAREA). `SET ADDRESS OF` (direct or through a pointer) is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5283,13 +5283,19 @@ def commarea_alternative_todos(commarea: dict) -> list[str]:
|| o instanceof Double || o instanceof Float);
}

/** #4956: whether `part` is `whole` or an object `whole` holds (a nested group, a table, an element, a byte
/** #5060: a String or number `part` cannot be traced by reference, so it counts when it equals a value `whole`
* holds (an equal-valued copy is refused too: refuse, never guess).
* #4956: whether `part` is `whole` or an object `whole` holds (a nested group, a table, an element, a byte
* area), by reference -- what an XCTL of a LINKed program's DTO, or of a part of it, passes. */
private static boolean within(Object whole, Object part, java.util.IdentityHashMap<Object, Boolean> seen) {
if (whole == part) {
return true;
}
if (!aliasable(whole) || seen.put(whole, Boolean.TRUE) != null) {
boolean byValue = part != null && !aliasable(part); // #5060: a value is traced by equality, not identity
if (!aliasable(whole)) {
return byValue && part.equals(whole);
}
if (seen.put(whole, Boolean.TRUE) != null) {
return false;
}
Class<?> type = whole.getClass();
Expand All @@ -5308,7 +5314,7 @@ def commarea_alternative_todos(commarea: dict) -> list[str]:
} else if (!type.getName().startsWith("java.")) {
for (Class<?> k = type; k != null && k != Object.class; k = k.getSuperclass()) {
for (java.lang.reflect.Field f : k.getDeclaredFields()) {
if (java.lang.reflect.Modifier.isStatic(f.getModifiers()) || f.getType().isPrimitive()) {
if (java.lang.reflect.Modifier.isStatic(f.getModifiers()) || (f.getType().isPrimitive() && !byValue)) {
continue;
}
try {
Expand Down Expand Up @@ -5357,7 +5363,7 @@ def commarea_alternative_todos(commarea: dict) -> list[str]:
snapshot.apply(commarea), "resp", resp, "resp2", resp2);
boolean ofLinkage = xctlOfLinkage;
xctlOfLinkage = false;
boolean ofDto = linkedDto && aliasable(commarea) && within(this.commarea, commarea, new java.util.IdentityHashMap<>());
boolean ofDto = linkedDto && commarea != null && within(this.commarea, commarea, new java.util.IdentityHashMap<>());
if ("NORMAL".equals(resp) && (ofLinkage && callerArea || ofDto)) {
throw new UnsupportedOperationException("XCTL passing the COMMAREA its LINK caller passed: IBM does not say "
+ "whether the target addresses the caller's storage or a copy, not modelled");
Expand Down
25 changes: 21 additions & 4 deletions tests/cobol_mainframe/test_model_link_xctl_4956.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
area). IBM (EXEC CICS XCTL, COMMAREA: "the contents of the data-area are passed") does not say whether the target then
addresses the caller's storage or a copy. A copy (a new object), a failed XCTL, an XCTL at level 1 or of a task's own
start COMMAREA, and the target of an XCTL passing its own DTO again (a copy of a copy) are not refused. A String or
number field is a value, not an object the caller holds: it cannot be told from a copy and is passed as it is.
number cannot be traced by reference (#5060): one equal to a value the DTO holds is refused too, an equal-valued copy
included (refuse, never guess); a value the DTO does not hold is passed as it is.
"""

from __future__ import annotations
Expand Down Expand Up @@ -62,6 +63,12 @@
case "mapped" -> d.byKey.get("k");
case "bytes" -> d.raw;
case "name" -> d.name;
case "number" -> d.num;
case "nested" -> d.grp.text;
case "tabled" -> d.table[1].text;
case "keyed" -> d.byKey.get("k").text;
case "fresh" -> "zzz";
case "none" -> null;
case "copy", "hop" -> new Dto();
default -> d;
};
Expand Down Expand Up @@ -96,7 +103,10 @@
for (String s : new String[] {"whole", "group", "table", "element", "listed", "mapped", "bytes"}) {
System.out.println(s + " " + run(s, false, true));
}
for (String s : new String[] {"name", "copy", "failed", "hop"}) {
for (String s : new String[] {"name", "number", "nested", "tabled", "keyed"}) {
System.out.println(s + " " + run(s, false, true));
}
for (String s : new String[] {"copy", "fresh", "none", "failed", "hop"}) {
System.out.println(s + " " + run(s, false, true));
}
System.out.println("level1 " + run("whole", true, true));
Expand All @@ -114,7 +124,8 @@ def _jdk() -> Path | None:
@pytest.mark.skipif(_jdk() is None, reason="no JDK (JDK_17 / JAVA_HOME)")
def test_cictask_refuses_an_xctl_of_the_dto_its_program_was_linked_with(tmp_path):
"""The whole DTO and every object it holds (group, table, element, list element, map value, byte area) are the
caller's storage: refused. A copy, a failed XCTL (the program goes on), a String field (a value), a copy-of-a-copy
caller's storage: refused; so is a String or number equal to a value the DTO holds (#5060: a value cannot be traced,
so an equal-valued copy is refused too). A copy, a distinct value, no COMMAREA, a failed XCTL (the program goes on), a copy-of-a-copy
hop, a level-1 task and a LINK that passed no DTO are not; the DTO holding itself does not loop."""
from gitgalaxy.tools.cobol_to_java.cobol_to_java_transaction_forge import CICS_SPEC_JAVA, CICS_TASK_JAVA

Expand All @@ -138,8 +149,14 @@ def test_cictask_refuses_an_xctl_of_the_dto_its_program_was_linked_with(tmp_path
"listed REFUSED true",
"mapped REFUSED true",
"bytes REFUSED true",
"name NORMAL>EE",
"name REFUSED true",
"number REFUSED true",
"nested REFUSED true",
"tabled REFUSED true",
"keyed REFUSED true",
"copy NORMAL>EE",
"fresh NORMAL>EE",
"none NORMAL>EE",
"failed PGMIDERR",
"hop NORMAL>EE",
"level1 NORMAL>EE",
Expand Down
Loading