Skip to content

Conversation

@tsh-hashimoto
Copy link
Contributor

@tsh-hashimoto tsh-hashimoto commented Nov 17, 2025

事象

  • 集団項目にEXTERNALが付いている場合、下位項目にMOVEをすると先頭バイトに値がセットされてしまう。

何が問題か

  • 集団項目にEXTERNALを付けた場合、下位項目に対応するCobolDataStorage型の変数が出力されない。

変更点

主要な変更点を以下に書く。

まず、EXTERNAL付きの集団項目の下位項目が出力されるように以下の条件分岐を無効にした。

  • joutput_base
  // if (!top->flag_external) {
    register_data_storage_list(f, top);
  // }

これは、NullpointerException を引き起こす。init() メソッドでAbstractCobolField 型の変数(f_~~~)に CobolDataStorage 型の変数(b_~~~)を setDataStorage でセットしているが、CobolDataStorage 型の変数はこの時点ではnullなので、この例外が発生してしまう。実際、CobolExternal.getStorageAddress()run_module() で実行されるが、これは init() メソッドの後に実行される。これを解消するために、CobolExternal.getStorageAddress()init() メソッドで出力されるようにした。

  • joutput_init_method ... else if節を追加して、ここで CobolExternal.getStorageAddress を出力するようにした。
  /* CobolDataStorage型変数の初期化(定数) */
  if (base_cache) {
    joutput_line("/* Data storage */\n");
    joutput_line("cob_unifunc = null;\n");
    base_cache = list_cache_sort(base_cache, &base_cache_cmp);
    prevprog = NULL;
    // EDIT
    for (blp = base_cache; blp; blp = blp->next) {
      char *base_name = get_java_identifier_base(blp->f);
      if (blp->curr_prog != prevprog) {
        prevprog = blp->curr_prog;
        joutput_prefix();
        joutput("/* PROGRAM-ID : %s */\n", prevprog);
        joutput_prefix();
        joutput("%s = new CobolDataStorage(%d);", base_name,
                blp->f->memory_size);
      } else if (blp->f->flag_external) {
        joutput_initialize_external(cb_build_field_reference(blp->f, NULL), blp->f);
      } else {
        joutput_prefix();
        joutput("%s = new CobolDataStorage(%d);", base_name,
                blp->f->memory_size);
      }
      free(base_name);
      joutput("\t/* %s */\n", blp->f->name);
    }
  • joutput_initialize ... run_module 関数に CobolExternal.getStorageAddress を出力する以下の処理を削除した。
  if (f->flag_external) {
    joutput_initialize_external(p->var, f);
    if (!p->flag_statement) {
      return;
    }
  }

また、base_cacheにEXTERNALの変数が加えられていなかったため、そこも修正した。

  • joutput_base ... if(!top->flag_external) の分岐を無効にした。
  if (!top->flag_base) {
    // if (!top->flag_external) {
      if (!top->flag_local || top->flag_is_global) {
        bl = cobc_malloc(sizeof(struct base_list));
        bl->f = top;
        bl->curr_prog = excp_current_program_id;
        bl->next = base_cache;
        base_cache = bl;
      } else {
        if (current_prog->flag_global_use) {
          joutput_local("unsigned char\t\t*%s%s = NULL;", CB_PREFIX_BASE, name);
          joutput_local("unsigned char\t\t*%s%s = NULL;", CB_PREFIX_BASE, name);
          joutput_local("\t/* %s */\n", top->name);
          joutput_local("static unsigned char\t*save_%s%s;\n", CB_PREFIX_BASE,
                        name);
        } else {
          joutput_local("unsigned char\t*%s%s = NULL;", CB_PREFIX_BASE, name);
          joutput_local("\t/* %s */\n", top->name);
        }
      }
    // }
    top->flag_base = 1;
  }

ここまでの修正だと、CobolDataStorage 型の変数が重複して宣言されてしまっていたので、これを修正した。

  • joutput_declare_member_variables ... 以下の記述をコメントアウト
  /* External items */
  for (f = prog->working_storage; f; f = f->sister) {
    if (f->flag_external) {
      joutput_prefix();
      joutput("private CobolDataStorage ");
      joutput_base(f);
      joutput(" = null;  /* %s */", f->name);
      joutput_newline();
    }
  }
  for (l = prog->file_list; l; l = CB_CHAIN(l)) {
    f = CB_FILE(CB_VALUE(l))->record;
    if (f->flag_external) {
      joutput_prefix();
      joutput("private CobolDataStorage ");
      joutput_base(f);
      joutput(" = null;  /* %s */", f->name);
      joutput_newline();
    }
  }

run_module 関数の初期化処理で fillBytes を使用してスペース埋めや0埋めをしているが、複数のプログラムでEXTERNALの変数を共有しているときに、ここで初期化されてしまうと想定外の値になってしまうので、EXTERNALの値はfillBytesが実行されないようにした。

  • joutput_initial_values ... EXTERNALのときは処理をスキップするようにした。
    /* EXTERNAL items */
    if (p->flag_external) {
      continue;
    }

テスト

run/miscellaneous.at

  • EXTERNAL data item / EXTERNAL AS data item
    • スキップされていたが、有効化してパスすることが確認できている。
  • EXTERNAL group data item
    • 新規に追加した。集団項目にEXTERNALが付いている場合のテスト。

* Fix a bug about data items with EXTERNAL
* Enable the skipped tests about EXTERNAL
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes bugs related to EXTERNAL data items in COBOL code generation. The changes enable previously skipped tests and refactor how external data items are handled in the Java code generator.

Key changes:

  • Enables two previously skipped tests for EXTERNAL data items by removing AT_CHECK([${SKIP_TEST}]) and fixing test commands
  • Refactors external data item handling to treat them more uniformly with non-external items
  • Moves external item initialization from early stages to the init method

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 9 comments.

File Description
tests/run.src/miscellaneous.at Enables EXTERNAL data item tests and corrects test execution commands from java prog to ${RUN_MODULE} caller
cobj/codegen.c Refactors external data item handling by removing conditional branches, moving initialization logic, and preventing duplicate initialization

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@tsh-hashimoto tsh-hashimoto marked this pull request as ready for review November 17, 2025 07:30
Copy link
Contributor

@yutaro-sakamoto yutaro-sakamoto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • AT_SETUP([Nihongo field name in extaddr test msg.])
    AT_CHECK([${SKIP_TEST}])
    AT_DATA([prog.cob], [
    IDENTIFICATION DIVISION.
    PROGRAM-ID. check1.
    DATA DIVISION.
    WORKING-STORAGE SECTION.
    01 X項目 PIC X(5) EXTERNAL.
    PROCEDURE DIVISION.
    CALL 'check2'.
    IDENTIFICATION DIVISION.
    PROGRAM-ID. check2.
    DATA DIVISION.
    WORKING-STORAGE SECTION.
    01 X項目 PIC X(6) EXTERNAL.
    PROCEDURE DIVISION.
    END PROGRAM check2.
    END PROGRAM check1.
    ])
    AT_CHECK([${COMPILE} -debug prog.cob])
    AT_CHECK([java prog], [1], [],
    [prog.cob:8: libcobj: EXTERNAL item 'X項目' has size > 6
    ])
    AT_CLEANUP
  • AT_SETUP([Nihongo field name in extaddr test msg.])
    export LC_ALL=''
    AT_CHECK([${SKIP_TEST}])
    AT_DATA([prog.cob], [
    IDENTIFICATION DIVISION.
    PROGRAM-ID. check1.
    DATA DIVISION.
    WORKING-STORAGE SECTION.
    01 X項目 PIC X(5) EXTERNAL.
    PROCEDURE DIVISION.
    CALL 'check2'.
    IDENTIFICATION DIVISION.
    PROGRAM-ID. check2.
    DATA DIVISION.
    WORKING-STORAGE SECTION.
    01 X項目 PIC X(6) EXTERNAL.
    PROCEDURE DIVISION.
    END PROGRAM check2.
    END PROGRAM check1.
    ])
    AT_CHECK([${COMPILE} -debug prog.cob])
    AT_CHECK([java prog 2> out1.txt], [1])
    AT_CHECK([echo "prog.cob:8: libcobj: EXTERNAL item 'X項目' has size > 6" | nkf --ic=UTF-8 --oc=Shift_JIS > out2.txt])
    AT_CHECK([diff out1.txt out2.txt])
    AT_CLEANUP

上記2つのスキップしているテストケースでも一部データにEXTERNALが使われていますが、今回の修正では解消しない不具合ということで合っていますか?

@tsh-hashimoto
Copy link
Contributor Author

@yutaro-sakamoto

#747 (review)
こちらのコメントのテストについて、そこにもEXTERNALがあることを見落としていました。
SKIP_TESTを削除して有効にしましたが、テストが失敗しました。

テストプログラムを見ると、prog.cobというプログラムの中にcheck1, check2という2つのPROGRAM-IDが書かれていますが、cobjでこれをコンパイルすると prog.class は生成されず、クラスファイルとして check1.classcheck2.class が生成されました(cobcではprog.cやprog.soが生成されました)。

この失敗自体はEXTERNALに問題があるわけではないので、プログラムをcheck1.cblとcheck2.cblの2つに分ければ通るような気はします。

@yutaro-sakamoto
Copy link
Contributor

yutaro-sakamoto commented Nov 18, 2025

@tsh-hashimoto
承知しました。そちらは別のPRで修正することにしましょう

@yutaro-sakamoto yutaro-sakamoto merged commit 2664dd9 into opensourcecobol:develop Nov 18, 2025
145 checks passed
@tsh-hashimoto
Copy link
Contributor Author

#747 (comment)

スキップしたテストについて
プログラムを2つに分けて実行しましたが、想定されているエラーは出ずに正常終了しました。
cobcの方では想定通りエラーが出ました。cobcの方は common.c/cob_external_addr でEXTERNALのデータ項目のサイズのチェックをしていましたが、COBOL 4Jにはそのようなチェック処理はありませんでした。

この件については新たにIssueを作成します。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants