Skip to content

Commit 303a3b3

Browse files
Fix Java code generation for USAGE POINTER fields (#802)
* fix: fix Java code generation for USAGE POINTER fields * Replace C-style pointer cast `(*(unsigned char **) (...))` with `.longValue()` in joutput_integer for proper Java code generation * Handle VALUE NULL assignment to POINTER fields by outputting `0L` instead of `null` to avoid ambiguous method call on CobolDataStorage.set() * fix: fix Java code generation for USAGE POINTER fields - Replace C-style pointer cast in joutput_integer with .longValue() - Output 0L instead of null for cb_null in integer context - Generate reference assignment (b_Y = ...) for SET ADDRESS OF - Use CobolPointerRegistry to map between pointer IDs (long) and CobolDataStorage references for SET/IF with POINTER variables - Enable and update the pointer.at test case * fix: format CobolPointerRegistry.java * fix: wrap POINTER value with primitiveToDataStorage in CALL BY VALUE - The CALL statement with BY VALUE for POINTER fields was generating a raw long value, but CobolRunnable.run() expects CobolDataStorage[]. Wrap the value with CobolDataStorage.primitiveToDataStorage() and add a long overload of that method for 8-byte pointer values. * doc: fix Javadoc for CobolDataStorage.java * test: add test for USAGE POINTER * fix: clear CobolPointerRegistry on STOP RUN to prevent memory leak * fix: apply Copilot review * fix: format codegen.c * fix: apply Claude review * fix: support resolving pointer after SET PTR UP/DOWN BY n * test: add pointer arithmetic test with multiple UP/DOWN BY and multi-byte dereference
1 parent d2d2758 commit 303a3b3

7 files changed

Lines changed: 854 additions & 11 deletions

File tree

cobj/codegen.c

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,7 @@ static void joutput_integer(cb_tree x) {
12781278
if (x == cb_zero) {
12791279
joutput("0");
12801280
} else if (x == cb_null) {
1281-
joutput("null");
1281+
joutput("0L");
12821282
} else {
12831283
joutput("%s", CB_CONST(x)->val);
12841284
}
@@ -1316,9 +1316,15 @@ static void joutput_integer(cb_tree x) {
13161316
cp = CB_CAST(x);
13171317
switch (cp->type) {
13181318
case CB_CAST_ADDRESS:
1319-
joutput("(");
1320-
joutput_data(cp->val);
1321-
joutput(")");
1319+
if (integer_reference_flag) {
1320+
joutput("(");
1321+
joutput_data(cp->val);
1322+
joutput(")");
1323+
} else {
1324+
joutput("CobolPointerRegistry.register(");
1325+
joutput_data(cp->val);
1326+
joutput(")");
1327+
}
13221328
break;
13231329
case CB_CAST_PROGRAM_POINTER:
13241330
joutput_func_1("CobolResolve.resolveToPointer", x);
@@ -1340,9 +1346,10 @@ static void joutput_integer(cb_tree x) {
13401346
return;
13411347

13421348
case CB_USAGE_POINTER:
1343-
joutput("(*(unsigned char **) (");
13441349
joutput_data(x);
1345-
joutput("))");
1350+
if (!integer_reference_flag) {
1351+
joutput(".longValue()");
1352+
}
13461353
return;
13471354

13481355
case CB_USAGE_PROGRAM_POINTER:
@@ -3178,10 +3185,14 @@ static void joutput_call(struct cb_call *p) {
31783185
break;
31793186
case CB_USAGE_INDEX:
31803187
case CB_USAGE_LENGTH:
3181-
case CB_USAGE_POINTER:
31823188
case CB_USAGE_PROGRAM_POINTER:
31833189
joutput_integer(x);
31843190
break;
3191+
case CB_USAGE_POINTER:
3192+
joutput("CobolDataStorage.primitiveToDataStorage(");
3193+
joutput_integer(x);
3194+
joutput(")");
3195+
break;
31853196
default:
31863197
joutput("*(");
31873198
joutput_data(x);
@@ -3823,6 +3834,44 @@ static void joutput_stmt(cb_tree x, enum joutput_stmt_type output_type) {
38233834
case CB_TAG_ASSIGN:
38243835
ap = CB_ASSIGN(x);
38253836

3837+
/* SET ADDRESS OF <var> TO ... (reference assignment) */
3838+
if (CB_CAST_P(ap->var) && CB_CAST(ap->var)->type == CB_CAST_ADDRESS) {
3839+
/* Verify that the target is a 01/77 level item so that
3840+
joutput_data emits a simple assignable variable (e.g. b_L_G).
3841+
Subfields would generate getSubDataStorage() which is not
3842+
a valid assignment target in Java. */
3843+
cb_tree target_val = CB_CAST(ap->var)->val;
3844+
if (CB_REFERENCE_P(target_val)) {
3845+
struct cb_field *target_f = cb_field(target_val);
3846+
if (target_f->parent != NULL) {
3847+
fprintf(stderr, "SET ADDRESS OF is only supported for 01/77 level "
3848+
"LINKAGE items\n");
3849+
ABORT();
3850+
}
3851+
}
3852+
joutput_prefix();
3853+
joutput_data(target_val);
3854+
joutput(" = ");
3855+
if (ap->val == cb_null) {
3856+
joutput("null");
3857+
} else if (CB_CAST_P(ap->val) &&
3858+
CB_CAST(ap->val)->type == CB_CAST_ADDRESS) {
3859+
/* SET ADDRESS OF Y TO ADDRESS OF X */
3860+
joutput_data(CB_CAST(ap->val)->val);
3861+
} else {
3862+
/* SET ADDRESS OF Y TO PTR */
3863+
joutput("CobolPointerRegistry.resolve(");
3864+
joutput_integer(ap->val);
3865+
joutput(")");
3866+
}
3867+
if (output_type == JOUTPUT_STMT_TRIM) {
3868+
joutput("\n");
3869+
} else {
3870+
joutput(";\n");
3871+
}
3872+
break;
3873+
}
3874+
38263875
joutput_prefix();
38273876

38283877
int tmp_flag = integer_reference_flag;
@@ -3847,7 +3896,17 @@ static void joutput_stmt(cb_tree x, enum joutput_stmt_type output_type) {
38473896
}
38483897

38493898
++index_read_flag;
3850-
joutput_integer(ap->val);
3899+
if (f->usage == CB_USAGE_POINTER && ap->val == cb_null) {
3900+
joutput("0L");
3901+
} else if (f->usage == CB_USAGE_POINTER && CB_CAST_P(ap->val) &&
3902+
CB_CAST(ap->val)->type == CB_CAST_ADDRESS) {
3903+
/* SET PTR TO ADDRESS OF X */
3904+
joutput("CobolPointerRegistry.register(");
3905+
joutput_data(CB_CAST(ap->val)->val);
3906+
joutput(")");
3907+
} else {
3908+
joutput_integer(ap->val);
3909+
}
38513910
--index_read_flag;
38523911
if (output_type == JOUTPUT_STMT_TRIM) {
38533912
joutput(")\n");

libcobj/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ SRC_FILES = \
3838
./app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolGroupField.java \
3939
./app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolNationalField.java \
4040
./app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolNumericEditedField.java \
41+
./app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolPointerRegistry.java \
4142
./app/src/main/java/jp/osscons/opensourcecobol/libcobj/exceptions/CobolExceptionId.java \
4243
./app/src/main/java/jp/osscons/opensourcecobol/libcobj/exceptions/CobolStopRunException.java \
4344
./app/src/main/java/jp/osscons/opensourcecobol/libcobj/exceptions/CobolExceptionInfo.java \

libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolDataStorage.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,19 @@ public static CobolDataStorage primitiveToDataStorage(int n) {
162162
return new CobolDataStorage(bytes);
163163
}
164164

165+
/**
166+
* TODO: 準備中
167+
*
168+
* @param n TODO: 準備中
169+
* @return TODO: 準備中
170+
*/
171+
public static CobolDataStorage primitiveToDataStorage(long n) {
172+
byte[] bytes = new byte[8];
173+
ByteBuffer buffer = ByteBuffer.wrap(bytes);
174+
buffer.putLong(n);
175+
return new CobolDataStorage(bytes);
176+
}
177+
165178
/**
166179
* TODO: 準備中
167180
*
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Copyright (C) 2021-2022 TOKYO SYSTEM HOUSE Co., Ltd.
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public License
6+
* as published by the Free Software Foundation; either version 3.0,
7+
* or (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with this library; see the file COPYING.LIB. If
16+
* not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor
17+
* Boston, MA 02110-1301 USA
18+
*/
19+
package jp.osscons.opensourcecobol.libcobj.data;
20+
21+
import java.util.HashMap;
22+
import java.util.IdentityHashMap;
23+
import java.util.Map;
24+
25+
/**
26+
* COBOL USAGE POINTER のアドレス値と CobolDataStorage の対応を管理するレジストリ。
27+
*
28+
* <p>ポインタ値は long の上位32ビットにバイト配列ID、下位32ビットにオフセット(インデックス)を
29+
* エンコードする。これにより SET PTR UP/DOWN BY n のポインタ演算が自然に動作する。
30+
*/
31+
public final class CobolPointerRegistry {
32+
/** 次に割り当てるバイト配列IDのカウンタ */
33+
private static int nextId = 1;
34+
35+
/** バイト配列ID → バイト配列の逆引きマップ (resolveで使用) */
36+
private static final Map<Integer, byte[]> idToByteArray = new HashMap<>();
37+
38+
/** バイト配列 → バイト配列IDの正引きマップ (registerで使用、IdentityHashMapで参照同一性を比較) */
39+
private static final IdentityHashMap<byte[], Integer> byteArrayToId = new IdentityHashMap<>();
40+
41+
/** バイト配列 → (インデックス → CobolDataStorage) のキャッシュ。同じ配列・同じオフセットのインスタンスを再利用する */
42+
private static final IdentityHashMap<byte[], Map<Integer, CobolDataStorage>>
43+
cobolDataStorageCache = new IdentityHashMap<>();
44+
45+
private CobolPointerRegistry() {}
46+
47+
/**
48+
* CobolDataStorage を登録し、対応するアドレス値(long)を返す。
49+
*
50+
* <p>アドレス値は上位32ビットにバイト配列ID、下位32ビットにインデックスをエンコードした値。
51+
* 同じバイト配列・同じインデックスの場合は同じアドレス値を返す。
52+
*
53+
* @param s 登録する CobolDataStorage (null の場合は 0L を返す)
54+
* @return アドレス値
55+
*/
56+
public static long register(CobolDataStorage s) {
57+
if (s == null) {
58+
// NULLポインタは0Lで表現する
59+
return 0L;
60+
}
61+
62+
byte[] data = s.getRefOfData();
63+
int index = s.getIndex();
64+
65+
// バイト配列が未登録であれば新しいIDを割り当てて双方向マップに登録する
66+
Integer byteArrayId = byteArrayToId.get(data);
67+
if (byteArrayId == null) {
68+
byteArrayId = nextId++;
69+
idToByteArray.put(byteArrayId, data);
70+
byteArrayToId.put(data, byteArrayId);
71+
}
72+
73+
// CobolDataStorageインスタンスをキャッシュに登録する (同じ配列・オフセットは初回のみ)
74+
Map<Integer, CobolDataStorage> indexCache =
75+
cobolDataStorageCache.computeIfAbsent(data, k -> new HashMap<>());
76+
indexCache.putIfAbsent(index, s);
77+
78+
// 上位32ビット: バイト配列ID、下位32ビット: オフセット にエンコードして返す
79+
return (((long) byteArrayId) << 32) | (0x00000000ffffffffL & index);
80+
}
81+
82+
/**
83+
* アドレス値から CobolDataStorage を取得する。
84+
*
85+
* <p>ポインタ演算 (SET PTR UP/DOWN BY n) された値にも対応する。 上位32ビットからバイト配列IDを、下位32ビットからオフセットを取り出し、
86+
* 対応する CobolDataStorage を返す。キャッシュにヒットした場合は既存インスタンスを再利用する。
87+
*
88+
* @param id アドレス値 (0L の場合は null を返す)
89+
* @return 対応する CobolDataStorage
90+
* @throws IllegalArgumentException 未登録のバイト配列IDが指定された場合
91+
*/
92+
public static CobolDataStorage resolve(long id) {
93+
if (id == 0L) {
94+
// NULLポインタ
95+
return null;
96+
}
97+
98+
// アドレス値を上位32ビット(バイト配列ID)と下位32ビット(オフセット)に分解する
99+
int byteArrayId = (int) (id >>> 32);
100+
int index = (int) (0x00000000ffffffffL & id);
101+
102+
// バイト配列IDから元のバイト配列を取得する
103+
byte[] data = idToByteArray.get(byteArrayId);
104+
if (data == null) {
105+
throw new IllegalArgumentException(
106+
"Invalid pointer id "
107+
+ id
108+
+ ": no byte array registered for byteArrayId "
109+
+ byteArrayId
110+
+ ".");
111+
}
112+
113+
// キャッシュに既存のCobolDataStorageがあればそれを返す
114+
Map<Integer, CobolDataStorage> indexCache = cobolDataStorageCache.get(data);
115+
if (indexCache != null) {
116+
CobolDataStorage cached = indexCache.get(index);
117+
if (cached != null) {
118+
return cached;
119+
}
120+
}
121+
122+
// キャッシュにない場合は新規作成してキャッシュに登録する
123+
// (ポインタ演算でオフセットが変わった場合にここに到達する)
124+
CobolDataStorage created = new CobolDataStorage(data, index);
125+
if (indexCache == null) {
126+
indexCache = new HashMap<>();
127+
cobolDataStorageCache.put(data, indexCache);
128+
}
129+
indexCache.put(index, created);
130+
return created;
131+
}
132+
133+
/**
134+
* レジストリを初期状態にリセットする。
135+
*
136+
* <p>STOP RUN 時に呼び出し、登録済みのポインタ情報をすべて解放してメモリリークを防止する。
137+
*/
138+
public static void clear() {
139+
idToByteArray.clear();
140+
byteArrayToId.clear();
141+
cobolDataStorageCache.clear();
142+
nextId = 1;
143+
}
144+
}

libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/exceptions/CobolStopRunException.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package jp.osscons.opensourcecobol.libcobj.exceptions;
2020

2121
import jp.osscons.opensourcecobol.libcobj.data.CobolDataStorage;
22+
import jp.osscons.opensourcecobol.libcobj.data.CobolPointerRegistry;
2223
import jp.osscons.opensourcecobol.libcobj.file.CobolFile;
2324

2425
/** STOP RUNの呼び出し時にスローされる例外。返り値を保持する。 */
@@ -91,5 +92,6 @@ public static void stopRun() {
9192
// TODO screen実装時に追加
9293
// cob_screen_terminate();
9394
CobolFile.exitFileIO();
95+
CobolPointerRegistry.clear();
9496
}
9597
}

0 commit comments

Comments
 (0)