blob: df1c7dfb1d1a4841a4ff0d11022657765291fe15 [file] [log] [blame]
/*
* Copyright (c) 2021-2025 Symas Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of the Symas Corporation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cobol-system.h"
#include "coretypes.h"
#include "tree.h"
#include "../../libgcobol/ec.h"
#include "../../libgcobol/common-defs.h"
#include "util.h"
#include "cbldiag.h"
#include "symbols.h"
#include "inspect.h"
#include "../../libgcobol/io.h"
#include "genapi.h"
#include "gengen.h"
#include "../../libgcobol/exceptl.h"
#include "util.h"
#include "genutil.h"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
static const ec_descr_t *
ec_type_descr( ec_type_t type ) {
auto p = std::find( __gg__exception_table, __gg__exception_table_end, type );
if( p == __gg__exception_table_end ) {
cbl_internal_error("no such exception: 0x%x", type);
}
return p;
}
const char *
ec_type_str( ec_type_t type ) {
auto p = ec_type_descr(type);
return p->name;
}
ec_disposition_t
ec_type_disposition( ec_type_t type ) {
auto p = ec_type_descr(type);
return p->disposition;
}
static size_t
ec_level( ec_type_t ec ) {
if( ec == ec_all_e ) return 1;
if( 0 == (static_cast<unsigned int>(ec) & ~EC_ALL_E) ) return 2;
return 3;
}
void
cbl_enabled_exception_t::dump( int i ) const {
cbl_message(2, "cbl_enabled_exception_t: %2d {%s, %s, %zu}",
i,
location? "location" : " none",
ec_type_str(ec),
file );
}
void
cbl_enabled_exceptions_t::dump() const {
extern int yydebug;
int debug = 1;
std::swap(debug, yydebug); // dbgmsg needs yydebug
if( empty() ) {
dbgmsg("cbl_enabled_exceptions_t: no exceptions" );
std::swap(debug, yydebug);
return;
}
int i = 1;
for( auto& elem : *this ) { // cppcheck-suppress constVariableReference
dbgmsg("cbl_enabled_exceptions_t: %2d {%s, %s, %lu}",
i++,
elem.location? "with location" : " no location",
ec_type_str(elem.ec),
gb4(elem.file) );
}
std::swap(debug, yydebug);
}
// cppcheck-suppress-begin [useStlAlgorithm] because why?
uint32_t
cbl_enabled_exceptions_t::status() const {
uint32_t status_word = 0;
for( const auto& ena : *this ) {
status_word |= (EC_ALL_E & ena.ec );
}
return status_word;
}
// cppcheck-suppress-end useStlAlgorithm
std::vector<uint64_t>
cbl_enabled_exceptions_t::encode() const {
std::vector<uint64_t> encoded;
auto p = std::back_inserter(encoded);
for( const auto& ec : *this ) {
*p++ = ec.location;
*p++ = ec.ec;
*p++ = ec.file;
}
return encoded;
}
void
cbl_enabled_exceptions_t::turn_on_off( bool enabled,
bool location,
ec_type_t type,
const std::set<size_t>& files )
{
// Update current enabled ECs tree on leaving this function.
class update_parser_t {
const cbl_enabled_exceptions_t& ecs;
public:
explicit update_parser_t(const cbl_enabled_exceptions_t& ecs) : ecs(ecs) {}
~update_parser_t() {
tree ena = parser_compile_ecs(ecs.encode());
current_enabled_ecs(ena);
}
} update_parser(*this);
// A Level 3 EC is added unilaterally; it can't affect a higher level.
if( ec_level(type) == 3 ) {
if( files.empty() ) {
auto elem = cbl_enabled_exception_t(location, type);
apply(enabled, elem);
return;
}
for( size_t file : files ) {
auto elem = cbl_enabled_exception_t(location, type, file);
apply(enabled, elem);
}
return;
}
// A new Level 1 or Level 2 EC is likewise simply added.
if( enabled ) {
if( files.empty() ) {
auto elem = cbl_enabled_exception_t(location, type);
apply(enabled, elem);
return;
}
for( size_t file: files ) {
auto elem = cbl_enabled_exception_t(location, type, file);
apply(enabled, elem);
}
return;
}
assert(!enabled);
assert(ec_level(type) < 3);
/*
* >> TURN EC [files] CHECKING OFF
*/
if( files.empty() ) {
// A Level 1 EC with no files disables all ECs
if( type == ec_all_e ) {
clear();
return;
}
// Because TURN CHECKING OFF mentioned no files, Remove any matching
// Level-2 or Level-3 ECs, regardless of their files.
auto p = begin();
while( end() != (p = std::find_if( begin(), end(),
[ec = type]( const auto& elem ) {
return
elem.ec != ec_all_e &&
ec_cmp(ec, elem.ec); } )) ) {
erase(p);
}
// Keep the EC as an override if a higher-level would othewise apply.
p = std::find_if( begin(), end(),
[ec = type]( const auto& elem ) {
return
(elem.ec == ec_all_e || elem.ec < ec) &&
elem.file == 0 &&
ec_cmp(ec, elem.ec); } );
if( p != end() ) {
auto elem = cbl_enabled_exception_t(location, type);
apply(enabled, elem);
}
} else {
// Remove any matching or lower-level EC for the same file.
for( size_t file: files ) {
auto p = begin();
while( end() != (p = std::find_if( begin(), end(),
[ec = type, file]( const auto& elem ) {
return
// ec is higher level and matches
(ec == ec_all_e || ec <= elem.ec) &&
file == elem.file &&
ec_cmp(ec, elem.ec); } )) ) {
erase(p);
}
// Keep the EC as an override if a higher-level would othewise apply.
p = std::find_if( begin(), end(),
[ec = type, file]( const auto& elem ) {
return
(elem.ec == ec_all_e || elem.ec < ec) &&
file == elem.file &&
ec_cmp(ec, elem.ec); } );
if( p != end() ) {
auto elem = cbl_enabled_exception_t(location, type, file);
apply(enabled, elem);
}
}
}
return;
}
const cbl_enabled_exception_t *
cbl_enabled_exceptions_t::match( ec_type_t type, size_t file ) const {
auto output = enabled_exception_match( begin(), end(), type, file );
return output != end()? &*output : NULL;
}
bool
sort_supers_last( const cbl_declarative_t& a, const cbl_declarative_t& b ) {
if( symbol_at(a.section)->program == symbol_at(b.section)->program ) {
return a.section < b.section;
}
return symbol_at(a.section)->program > symbol_at(b.section)->program;
}
cbl_field_t * new_temporary_decl();
/*
* Generate the code to evaluate declaratives. This is the "secret
* section" right after END DECLARATIVES. Its name is
* _DECLARATIVES_EVAL, and it is performed after every statement that
* could raise an exception.
*
* The code calls the library routine __gg__match_exception, which
* compares the raised exception to the criteria set by the USE
* statements in the DECLARATIVES super-section. It returns an
* integer, which is an index to the label in the symbol table that
* defines the section for the matching USE criteria.
*
* The generated code is a sequence of IF statements comparing the
* returned integer to that of each declarative. If equal, that
* section is PERFORMed, and control branches to the end of this
* section, and thence back to the statement it came from.
*/
#include "../../libgcobol/io.h"
size_t current_file_index();
file_status_t current_file_handled_status();
void
declarative_runtime_match( const std::list<cbl_declarative_t>& declaratives,
cbl_label_t *lave )
{
if( getenv("GCOBOL_SHOW") )
{
fprintf(stderr, "( %d ) %s: \n", cobol_location().first_line, __func__);
}
if( getenv("GCOBOL_TRACE") )
{
gg_printf(">>>>>>( %d )(%s) declaratives: lave:%s\n",
build_int_cst_type(INT, cobol_location().first_line),
gg_string_literal(__func__),
gg_string_literal(lave->name),
NULL_TREE);
}
static auto yes = new_temporary(FldConditional);
static auto isection = new_temporary(FldNumericBin5);
static auto index = new_temporary(FldNumericBin5);
/*
* Generate a sequence of COBOL IF statements to match the Declarative's
* symbol table index to its performable section. The entire sequence is
* guarded by a runtime IF that evaluates to TRUE only if the "current EC" is
* nonzero. This way, when _DECLARATIVES_EVAL is performed, it does nothing
* if no EC was raised.
*/
IF( var_decl_exception_code, ne_op, integer_zero_node ) {
// Get declarative section index matching any raised EC.
parser_match_exception(index);
// Compare returned index to each section index.
for( const auto& dcl : declaratives ) {
parser_set_numeric( isection, dcl.section );
parser_relop( yes, index, eq_op, isection );
parser_if( yes );
auto section = cbl_label_of(symbol_at(dcl.section));
parser_push_exception();
parser_perform(section);
parser_pop_exception();
parser_label_goto(lave);
parser_else();
parser_fi();
}
}
ELSE {
if( getenv("GCOBOL_TRACE") )
{
gg_printf(">>>>>>( %d )(%s) __gg__exception_code is zero\n",
build_int_cst_type(INT, cobol_location().first_line),
gg_string_literal(__func__),
NULL_TREE);
}
}
ENDIF
}
ec_type_t
ec_type_of( const cbl_name_t name ) {
auto p = std::find_if( __gg__exception_table, __gg__exception_table_end,
[name]( const ec_descr_t& descr ) {
return 0 == strcasecmp(name, descr.name);
} );
return p == __gg__exception_table_end? ec_none_e : p->type;
}