
(********************************************************************)
(*                                                                  *)
(*  destr.s7i     Generate code for the destructor functions.       *)
(*  Copyright (C) 1990 - 1994, 2004 - 2014  Thomas Mertes           *)
(*                                                                  *)
(*  This file is part of the Seed7 compiler.                        *)
(*                                                                  *)
(*  This program is free software; you can redistribute it and/or   *)
(*  modify it under the terms of the GNU General Public License as  *)
(*  published by the Free Software Foundation; either version 2 of  *)
(*  the License, or (at your option) any later version.             *)
(*                                                                  *)
(*  This program is distributed in the hope that it will be useful, *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of  *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *)
(*  GNU General Public License for more details.                    *)
(*                                                                  *)
(*  You should have received a copy of the GNU General Public       *)
(*  License along with this program; if not, write to the           *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


var typeReferenceHash: destrFunction is typeReferenceHash.EMPTY_HASH;
var boolean_type_hash: destr_declared is boolean_type_hash.EMPTY_HASH;
var boolean_type_hash: destr_prototype_declared is boolean_type_hash.EMPTY_HASH;
var string_type_hash: parametersOfHshDestr is string_type_hash.EMPTY_HASH;


const proc: process_destr_declaration (in type: object_type,
    inout expr_type: c_expr) is forward;


const proc: process_destr_call (in type: object_type,
    in string: param_b, inout string: expr) is forward;


const proc: process_arr_destr_declaration (in type: object_type,
    inout expr_type: c_expr) is func

  local
    var string: diagnosticLine is "";
    var string: select_value is "";
  begin
    if object_type in destrFunction then
      diagnosticLine := diagnosticLine(destrFunction[object_type]);
    end if;
    select_value := select_value_from_rtlObjectStruct(array_element[object_type]);
    process_destr_declaration(array_element[object_type], c_expr);
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "static void destr_";
    c_expr.expr &:= str(typeNumber(object_type));
    c_expr.expr &:= " (const_";
    c_expr.expr &:= type_name(object_type);
    c_expr.expr &:= " b)\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "{\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "memSizeType i;\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "memSizeType size;\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "if (b != NULL) {\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "size = (uintType)(b->max_position - b->min_position + 1);\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "for (i = 0; i < size; i++) {\n";
    c_expr.expr &:= diagnosticLine;
    process_destr_call(array_element[object_type],
        "b->arr[i]" & select_value, c_expr.expr);
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "arrFree((arrayType)(b));\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= noDiagnosticLine;
    c_expr.expr &:= "\n";
    destr_declared @:= [object_type] TRUE;
  end func;


const proc: process_sct_destr_declaration (in type: object_type,
    inout expr_type: c_expr) is func

  local
    var string: diagnosticLine is "";
    var number_element_hash: elements is number_element_hash.EMPTY_HASH;
    var integer: number is 0;
    var reference: struct_elem is NIL;
    var string: select_value is "";
  begin
    if object_type in destrFunction then
      diagnosticLine := diagnosticLine(destrFunction[object_type]);
    end if;
    if object_type in struct_elements then
      elements := flip(struct_elements[object_type]);
    end if;
    for number range 0 to pred(length(elements)) do
      struct_elem := elements[number][1];
      process_destr_declaration(struct_element_type[object_type][struct_elem], c_expr);
    end for;
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "static void destr_";
    c_expr.expr &:= str(typeNumber(object_type));
    c_expr.expr &:= " (";
    c_expr.expr &:= type_name(object_type);
    c_expr.expr &:= " b)\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "{\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "if (b != NULL && b->usage_count != 0) {\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "b->usage_count--;\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "if (b->usage_count == 0) {\n";
    for number range 0 to pred(length(elements)) do
      struct_elem := elements[number][1];
      select_value := select_value_from_rtlObjectStruct(struct_element_type[object_type][struct_elem]);
      c_expr.expr &:= diagnosticLine;
      process_destr_call(struct_element_type[object_type][struct_elem],
          "b->stru[" & str(number) & "]" & select_value, c_expr.expr);
    end for;
    if structFreelist then
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "if (flist_allowed_";
      c_expr.expr &:= str(typeNumber(object_type));
      c_expr.expr &:= ">0) {\n";
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "((freeListElemType)(b))->next = flist_";
      c_expr.expr &:= str(typeNumber(object_type));
      c_expr.expr &:= ";\n";
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "flist_";
      c_expr.expr &:= str(typeNumber(object_type));
      c_expr.expr &:= " = (freeListElemType)(b);\n";
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "flist_allowed_";
      c_expr.expr &:= str(typeNumber(object_type));
      c_expr.expr &:= "--;\n";
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "} else {\n";
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "free((void *)(b));\n";
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "flist_was_full_";
      c_expr.expr &:= str(typeNumber(object_type));
      c_expr.expr &:= "=1;\n";
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "}\n";
    else
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "free((void *)(b));\n";
      c_expr.expr &:= diagnosticLine;
    end if;
    c_expr.expr &:= "}}\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= noDiagnosticLine;
    c_expr.expr &:= "\n";
    destr_declared @:= [object_type] TRUE;
  end func;


const proc: process_hsh_destr_declaration (in type: object_type,
    inout expr_type: c_expr) is func

  local
    var string: diagnosticLine is "";
  begin
    if object_type in destrFunction then
      diagnosticLine := diagnosticLine(destrFunction[object_type]);
    end if;
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "static void destr_";
    c_expr.expr &:= str(typeNumber(object_type));
    c_expr.expr &:= " (const_";
    c_expr.expr &:= type_name(object_type);
    c_expr.expr &:= " b)\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "{\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "hshDestr(b";
    c_expr.expr &:= parametersOfHshDestr[object_type];
    c_expr.expr &:= ");\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= noDiagnosticLine;
    c_expr.expr &:= "\n";
    destr_declared @:= [object_type] TRUE;
  end func;


const proc: process_itf_destr_declaration (in type: object_type,
    inout expr_type: c_expr) is func

  local
    var string: diagnosticLine is "";
    var type: implementationType is void;
  begin
    if object_type in destrFunction then
      diagnosticLine := diagnosticLine(destrFunction[object_type]);
    end if;
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "static void destr_";
    c_expr.expr &:= str(typeNumber(object_type));
    c_expr.expr &:= " (";
    c_expr.expr &:= type_name(object_type);
    c_expr.expr &:= " b)\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "{\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "if (b != NULL) {\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "if (b->usage_count >= 2) {\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "b->usage_count--;\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "} else if (b->usage_count != 0) {\n";
    c_expr.expr &:= diagnosticLine;
    for implementationType range implements[object_type] do
      c_expr.expr &:= "if (b->type_num == ";
      c_expr.expr &:= str(typeNumber(implementationType));
      c_expr.expr &:= "/*";
      c_expr.expr &:= str(implementationType);
      c_expr.expr &:= "*/";
      c_expr.expr &:= ") {\n";
      c_expr.expr &:= diagnosticLine;
      process_destr_declaration(implementationType, global_c_expr);
      process_destr_call(implementationType, "b", c_expr.expr);
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "} else ";
    end for;
    c_expr.expr &:= "{\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "raise_error(ACTION_ERROR);\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= noDiagnosticLine;
    c_expr.expr &:= "\n";
    destr_declared @:= [object_type] TRUE;
  end func;


const proc: process_itf_destr_prototype (in type: object_type,
    inout expr_type: c_expr) is func

  begin
    if object_type not in destr_prototype_declared then
      c_expr.expr &:= "static void destr_";
      c_expr.expr &:= str(typeNumber(object_type));
      c_expr.expr &:= " (";
      c_expr.expr &:= type_name(object_type);
      c_expr.expr &:= ");\n\n";
      destr_prototype_declared @:= [object_type] TRUE;
    end if;
  end func;


const proc: declare_destr_prototype (in type: object_type,
    inout expr_type: c_expr) is func

  begin
    if object_type not in destr_prototype_declared then
      declare_type_if_necessary(object_type, c_expr);
      c_expr.expr &:= "static void destr_";
      c_expr.expr &:= str(typeNumber(object_type));
      c_expr.expr &:= " (const_";
      c_expr.expr &:= type_name(object_type);
      c_expr.expr &:= ");\n\n";
      destr_prototype_declared @:= [object_type] TRUE;
    end if;
  end func;


const proc: process_destr_declaration (in type: object_type,
    inout expr_type: c_expr) is func

  begin
    if object_type not in destr_declared then
      if object_type in typeCategory then
        case typeCategory[object_type] of
          when {ARRAYOBJECT}:
            if object_type in array_element then
              process_arr_destr_declaration(object_type, c_expr);
            else
              declare_destr_prototype(object_type, c_expr);
            end if;
          when {STRUCTOBJECT}:
            process_sct_destr_declaration(object_type, c_expr);
          when {HASHOBJECT}:
            if object_type in parametersOfHshDestr then
              process_hsh_destr_declaration(object_type, c_expr);
            else
              declare_destr_prototype(object_type, c_expr);
            end if;
          when {INTERFACEOBJECT}:
            process_itf_destr_prototype(object_type, c_expr);
          otherwise:
            destr_declared @:= [object_type] TRUE;
        end case;
      else
        declare_destr_prototype(object_type, c_expr);
      end if;
    end if;
  end func;


const proc: declare_missing_destr_declarations (inout expr_type: c_expr) is func

  local
    var type: object_type is void;
  begin
    for key object_type range destr_prototype_declared do
      process_destr_declaration(object_type, c_expr);
    end for;
    for key object_type range destr_prototype_declared do
      if object_type in typeCategory and typeCategory[object_type] = INTERFACEOBJECT then
        process_itf_destr_declaration(object_type, c_expr);
      end if;
    end for;
  end func;


const set of category: destrNecessary is {
    BIGINTOBJECT, STRIOBJECT, BSTRIOBJECT, SETOBJECT, REFLISTOBJECT,
    WINOBJECT, PROCESSOBJECT, PROGOBJECT, DATABASEOBJECT, SQLSTMTOBJECT,
    ARRAYOBJECT, HASHOBJECT, STRUCTOBJECT};


const proc: process_destr_call (in type: object_type,
    in string: param_b, inout string: expr) is func

  begin
    if object_type in typeCategory then
      case typeCategory[object_type] of
        when {BOOLOBJECT, ENUMOBJECT, INTOBJECT, FLOATOBJECT, CHAROBJECT,
              FILEOBJECT, SOCKETOBJECT, REFOBJECT, TYPEOBJECT,
              ACTOBJECT, BLOCKOBJECT}:
                                noop;
        when {BIGINTOBJECT}:    expr &:= "bigDestr(" & param_b & ");\n";
        when {STRIOBJECT}:      expr &:= "strDestr(" & param_b & ");\n";
        when {BSTRIOBJECT}:     expr &:= "bstDestr(" & param_b & ");\n";
        when {SETOBJECT}:       expr &:= "setDestr(" & param_b & ");\n";
        when {POLLOBJECT}:      expr &:= "polDestr(" & param_b & ");\n";
        when {REFLISTOBJECT}:   expr &:= "rflDestr(" & param_b & ");\n";
        when {WINOBJECT}:       expr &:= "drwDestr(" & param_b & ");\n";
        when {PROCESSOBJECT}:   expr &:= "pcsDestr(" & param_b & ");\n";
        when {PROGOBJECT}:      expr &:= "prgDestr(" & param_b & ");\n";
        when {DATABASEOBJECT}:  expr &:= "sqlDestrDb(" & param_b & ");\n";
        when {SQLSTMTOBJECT}:   expr &:= "sqlDestrStmt(" & param_b & ");\n";
        when {ARRAYOBJECT, HASHOBJECT, STRUCTOBJECT, INTERFACEOBJECT}:
          expr &:= "destr_";
          expr &:= str(typeNumber(object_type));
          expr &:= "(";
          expr &:= param_b;
          expr &:= ");\n";
      end case;
    else
      expr &:= "destr_";
      expr &:= str(typeNumber(object_type));
      expr &:= "((";
      expr &:= type_name(object_type);
      expr &:= ")(";
      expr &:= param_b;
      expr &:= "));\n";
    end if;
  end func;
