8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 21:23:04 +01:00
firebird-mirror/src/gpre/int_cxx.cpp
2009-01-14 08:22:32 +00:00

737 lines
18 KiB
C++

//____________________________________________________________
//
// PROGRAM: C preprocess
// MODULE: int_cxx.cpp
// DESCRIPTION: Code generate for internal JRD modules
//
// The contents of this file are subject to the Interbase Public
// License Version 1.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy
// of the License at http://www.Inprise.com/IPL.html
//
// Software distributed under the License is distributed on an
// "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
// or implied. See the License for the specific language governing
// rights and limitations under the License.
//
// The Original Code was created by Inprise Corporation
// and its predecessors. Portions created by Inprise Corporation are
// Copyright (C) Inprise Corporation.
//
// All Rights Reserved.
// Contributor(s): ______________________________________.
// TMN (Mike Nordell) 11.APR.2001 - Reduce compiler warnings in generated code
//
//
//____________________________________________________________
//
//
#include "firebird.h"
#include <stdio.h>
#include "../jrd/common.h"
#include <stdarg.h>
#include "../jrd/ibase.h"
#include "../gpre/gpre.h"
#include "../gpre/gpre_proto.h"
#include "../gpre/lang_proto.h"
#include "../jrd/gds_proto.h"
#include "../common/utils_proto.h"
static void align(const int);
static void asgn_from(ref*, int);
#ifdef NOT_USED_OR_REPLACED
static void asgn_to(ref*);
#endif
static void gen_at_end(const act*, int);
static void gen_blr(void*, SSHORT, const char*);
static void gen_compile(const gpre_req*, int);
static void gen_database(const act*, int);
static void gen_emodify(const act*, int);
static void gen_estore(const act*, int, bool);
static void gen_endfor(const act*, int);
static void gen_erase(const act*, int);
static void gen_for(const act*, int);
static char* gen_name(char* const, const ref*);
static void gen_raw(const gpre_req*);
static void gen_receive(const gpre_req*, const gpre_port*);
static void gen_request(const gpre_req*);
static void gen_routine(const act*, int);
static void gen_s_end(const act*, int);
static void gen_s_fetch(const act*, int);
static void gen_s_start(const act*, int);
static void gen_send(const gpre_req*, const gpre_port*, int, bool);
static void gen_start(const gpre_req*, const gpre_port*, int, bool);
static void gen_type(const act*, int);
static void gen_variable(const act*, int);
static void make_port(const gpre_port*, int);
static void printa(const int, const TEXT*, ...);
static bool global_first_flag = false;
const int INDENT = 3;
static const char* const GDS_VTOV = "gds__vtov";
static const char* const JRD_VTOF = "jrd_vtof";
static const char* const VTO_CALL = "%s ((const char*) %s, (char*) %s, %d);";
static inline void begin(const int column)
{
printa(column, "{");
}
static inline void endp(const int column)
{
printa(column, "}");
}
//____________________________________________________________
//
//
void INT_CXX_action( const act* action, int column)
{
// Put leading braces where required
switch (action->act_type)
{
case ACT_for:
case ACT_insert:
case ACT_modify:
case ACT_store:
case ACT_s_fetch:
case ACT_s_start:
begin(column);
align(column);
}
switch (action->act_type)
{
case ACT_at_end:
gen_at_end(action, column);
return;
case ACT_b_declare:
case ACT_database:
gen_database(action, column);
return;
case ACT_endfor:
gen_endfor(action, column);
break;
case ACT_endmodify:
gen_emodify(action, column);
break;
case ACT_endstore:
gen_estore(action, column, false);
break;
case ACT_endstore_special:
gen_estore(action, column, true);
break;
case ACT_erase:
gen_erase(action, column);
return;
case ACT_for:
gen_for(action, column);
return;
case ACT_hctef:
break;
case ACT_insert:
case ACT_routine:
gen_routine(action, column);
return;
case ACT_s_end:
gen_s_end(action, column);
return;
case ACT_s_fetch:
gen_s_fetch(action, column);
return;
case ACT_s_start:
gen_s_start(action, column);
break;
case ACT_type_number:
gen_type(action, column);
return;
case ACT_variable:
gen_variable(action, column);
return;
default:
return;
}
// Put in a trailing brace for those actions still with us
endp(column);
}
//____________________________________________________________
//
// Align output to a specific column for output.
//
static void align(const int column)
{
if (column < 0)
return;
putc('\n', gpreGlob.out_file);
int i;
for (i = column / 8; i; --i)
putc('\t', gpreGlob.out_file);
for (i = column % 8; i; --i)
putc(' ', gpreGlob.out_file);
}
//____________________________________________________________
//
// Build an assignment from a host language variable to
// a port variable.
//
static void asgn_from( ref* reference, int column)
{
TEXT variable[MAX_REF_SIZE];
TEXT temp[MAX_REF_SIZE];
for (; reference; reference = reference->ref_next)
{
const gpre_fld* field = reference->ref_field;
align(column);
gen_name(variable, reference);
const TEXT* value;
if (reference->ref_source) {
value = gen_name(temp, reference->ref_source);
}
else {
value = reference->ref_value;
}
// To avoid chopping off a double byte kanji character in between
// the two bytes, generate calls to gds__ftof2 gds$_vtof2,
// gds$_vtov2 and jrd_vtof2 wherever necessary
if (!field || field->fld_dtype == dtype_text)
fprintf(gpreGlob.out_file, VTO_CALL, JRD_VTOF, value, variable,
field ? field->fld_length : 0);
else if (!field || field->fld_dtype == dtype_cstring)
fprintf(gpreGlob.out_file, VTO_CALL, GDS_VTOV, value, variable,
field ? field->fld_length : 0);
else
fprintf(gpreGlob.out_file, "%s = %s;", variable, value);
}
}
//____________________________________________________________
//
// Build an assignment to a host language variable from
// a port variable.
//
#ifdef NOT_USED_OR_REPLACED
static void asgn_to( ref* reference)
{
TEXT s[MAX_REF_SIZE];
ref* source = reference->ref_friend;
gpre_fld* field = source->ref_field;
gen_name(s, source);
// Repeated later down in function gen_emodify, but then
// emitting jrd_ftof call.
if (!field || field->fld_dtype == dtype_text)
fprintf(gpreGlob.out_file, "gds__ftov (%s, %d, %s, sizeof(%s));",
s, field ? field->fld_length : 0, reference->ref_value, reference->ref_value);
else if (!field || field->fld_dtype == dtype_cstring)
fprintf(gpreGlob.out_file, "gds__vtov((const char*) %s, (char*) %s, sizeof(%s));",
s, reference->ref_value, reference->ref_value);
else
fprintf(gpreGlob.out_file, "%s = %s;", reference->ref_value, s);
}
#endif
//____________________________________________________________
//
// Generate code for AT END clause of FETCH.
//
static void gen_at_end( const act* action, int column)
{
TEXT s[MAX_REF_SIZE];
const gpre_req* request = action->act_request;
printa(column, "if (!%s) ", gen_name(s, request->req_eof));
}
//____________________________________________________________
//
// Callback routine for BLR pretty printer.
//
static void gen_blr(void* user_arg, SSHORT offset, const char* string)
{
fprintf(gpreGlob.out_file, "%s\n", string);
}
//____________________________________________________________
//
// Generate text to compile a request.
//
static void gen_compile( const gpre_req* request, int column)
{
column += INDENT;
//const gpre_dbb* db = request->req_database;
//const gpre_sym* symbol = db->dbb_name;
fprintf(gpreGlob.out_file, "if (!%s)", request->req_handle);
align(column);
fprintf(gpreGlob.out_file, "%s = CMP_compile2 (tdbb, (UCHAR*) jrd_%"ULONGFORMAT", TRUE);",
request->req_handle, request->req_ident);
}
//____________________________________________________________
//
// Generate insertion text for the database statement.
//
static void gen_database( const act* action, int column)
{
if (global_first_flag)
return;
global_first_flag = true;
align(0);
for (const gpre_req* request = gpreGlob.requests; request; request = request->req_next)
gen_request(request);
}
//____________________________________________________________
//
// Generate substitution text for END_MODIFY.
//
static void gen_emodify( const act* action, int column)
{
TEXT s1[MAX_REF_SIZE], s2[MAX_REF_SIZE];
const upd* modify = (upd*) action->act_object;
for (const ref* reference = modify->upd_port->por_references; reference;
reference = reference->ref_next)
{
const ref* source = reference->ref_source;
if (!source) {
continue;
}
const gpre_fld* field = reference->ref_field;
align(column);
switch (field->fld_dtype)
{
case dtype_text:
fprintf(gpreGlob.out_file, "jrd_ftof (%s, %d, %s, %d);",
gen_name(s1, source), field->fld_length, gen_name(s2, reference), field->fld_length);
break;
case dtype_cstring:
fprintf(gpreGlob.out_file, "gds__vtov((const char*) %s, (char*) %s, %d);",
gen_name(s1, source), gen_name(s2, reference), field->fld_length);
break;
default:
fprintf(gpreGlob.out_file, "%s = %s;", gen_name(s1, reference), gen_name(s2, source));
}
}
gen_send(action->act_request, modify->upd_port, column, false);
}
//____________________________________________________________
//
// Generate substitution text for END_STORE.
//
static void gen_estore( const act* action, int column, bool special)
{
const gpre_req* request = action->act_request;
align(column);
gen_compile(request, column);
gen_start(request, request->req_primary, column, special);
}
//____________________________________________________________
//
// Generate definitions associated with a single request.
//
static void gen_endfor( const act* action, int column)
{
const gpre_req* request = action->act_request;
column += INDENT;
if (request->req_sync)
gen_send(request, request->req_sync, column, false);
endp(column);
}
//____________________________________________________________
//
// Generate substitution text for ERASE.
//
static void gen_erase( const act* action, int column)
{
upd* erase = (upd*) action->act_object;
gen_send(erase->upd_request, erase->upd_port, column, false);
}
//____________________________________________________________
//
// Generate substitution text for FOR statement.
//
static void gen_for( const act* action, int column)
{
TEXT s[MAX_REF_SIZE];
gen_s_start(action, column);
align(column);
const gpre_req* request = action->act_request;
fprintf(gpreGlob.out_file, "while (1)");
column += INDENT;
begin(column);
align(column);
gen_receive(action->act_request, request->req_primary);
align(column);
fprintf(gpreGlob.out_file, "if (!%s) break;", gen_name(s, request->req_eof));
}
//____________________________________________________________
//
// Generate a name for a reference. Name is constructed from
// port and parameter idents.
//
static char* gen_name(char* const string, const ref* reference)
{
fb_utils::snprintf(string, MAX_REF_SIZE, "jrd_%d.jrd_%d",
reference->ref_port->por_ident, reference->ref_ident);
return string;
}
//____________________________________________________________
//
// Generate BLR in raw, numeric form. Ugly but dense.
//
static void gen_raw( const gpre_req* request)
{
TEXT buffer[80];
const UCHAR* blr = request->req_blr;
int blr_length = request->req_length;
TEXT* p = buffer;
align(0);
while (--blr_length) {
const UCHAR c = *blr++;
if ((c >= 'A' && c <= 'Z') || c == '$' || c == '_')
sprintf(p, "'%c',", c);
else
sprintf(p, "%d,", c);
while (*p)
p++;
if (p - buffer > 60) {
fprintf(gpreGlob.out_file, "%s\n", buffer);
p = buffer;
*p = 0;
}
}
fprintf(gpreGlob.out_file, "%s%d", buffer, blr_eoc);
}
//____________________________________________________________
//
// Generate a send or receive call for a port.
//
static void gen_receive( const gpre_req* request, const gpre_port* port)
{
fprintf(gpreGlob.out_file,
"EXE_receive (tdbb, %s, %d, %d, (UCHAR*) &jrd_%"ULONGFORMAT");",
request->req_handle, port->por_msg_number, port->por_length,
port->por_ident);
}
//____________________________________________________________
//
// Generate definitions associated with a single request.
//
static void gen_request( const gpre_req* request)
{
if (!(request->req_flags & REQ_exp_hand))
fprintf(gpreGlob.out_file, "static void\t*%s;\t// request handle \n", request->req_handle);
fprintf(gpreGlob.out_file, "static const UCHAR\tjrd_%"ULONGFORMAT" [%d] =",
request->req_ident, request->req_length);
align(INDENT);
fprintf(gpreGlob.out_file, "{\t// blr string \n");
if (gpreGlob.sw_raw)
gen_raw(request);
else
gds__print_blr(request->req_blr, gen_blr, 0, 0);
printa(INDENT, "};\t// end of blr string \n");
}
//____________________________________________________________
//
// Process routine head. If there are gpreGlob.requests in the
// routine, insert local definitions.
//
static void gen_routine( const act* action, int column)
{
for (const gpre_req* request = (gpre_req*) action->act_object; request;
request = request->req_routine)
{
for (const gpre_port* port = request->req_ports; port; port = port->por_next)
make_port(port, column + INDENT);
}
}
//____________________________________________________________
//
// Generate substitution text for END_STREAM.
//
static void gen_s_end( const act* action, int column)
{
const gpre_req* request = action->act_request;
printa(column, "EXE_unwind (tdbb, %s);", request->req_handle);
}
//____________________________________________________________
//
// Generate substitution text for FETCH.
//
static void gen_s_fetch( const act* action, int column)
{
const gpre_req* request = action->act_request;
if (request->req_sync)
gen_send(request, request->req_sync, column, false);
gen_receive(action->act_request, request->req_primary);
}
//____________________________________________________________
//
// Generate text to compile and start a stream. This is
// used both by START_STREAM and FOR
//
static void gen_s_start( const act* action, int column)
{
const gpre_req* request = action->act_request;
gen_compile(request, column);
const gpre_port* port = request->req_vport;
if (port)
asgn_from(port->por_references, column);
gen_start(request, port, column, false);
}
//____________________________________________________________
//
// Generate a send or receive call for a port.
//
static void gen_send( const gpre_req* request, const gpre_port* port, int column, bool special)
{
if (special) {
align(column);
fprintf(gpreGlob.out_file, "if (ignore_perm)");
align(column);
fprintf(gpreGlob.out_file, "\trequest->req_flags |= req_ignore_perm;");
}
align(column);
fprintf(gpreGlob.out_file, "EXE_send (tdbb, %s, %d, %d, (UCHAR*) &jrd_%"ULONGFORMAT");",
request->req_handle, port->por_msg_number, port->por_length, port->por_ident);
}
//____________________________________________________________
//
// Generate a START.
//
static void gen_start( const gpre_req* request, const gpre_port* port, int column, bool special)
{
align(column);
fprintf(gpreGlob.out_file, "EXE_start (tdbb, %s, %s);", request->req_handle, request->req_trans);
if (port)
gen_send(request, port, column, special);
}
//____________________________________________________________
//
// Substitute for a variable reference.
//
static void gen_type( const act* action, int column)
{
printa(column, "%ld", action->act_object);
}
//____________________________________________________________
//
// Substitute for a variable reference.
//
static void gen_variable( const act* action, int column)
{
char s[MAX_REF_SIZE];
align(column);
fprintf(gpreGlob.out_file, gen_name(s, action->act_object));
}
//____________________________________________________________
//
// Insert a port record description in output.
//
static void make_port( const gpre_port* port, int column)
{
printa(column, "struct {");
for (const ref* reference = port->por_references; reference; reference = reference->ref_next)
{
align(column + INDENT);
const gpre_fld* field = reference->ref_field;
const gpre_sym* symbol = field->fld_symbol;
const TEXT* name = symbol->sym_string;
const char* fmtstr = NULL;
switch (field->fld_dtype)
{
case dtype_short:
fmtstr = " SSHORT jrd_%d;\t// %s ";
break;
case dtype_long:
fmtstr = " SLONG jrd_%d;\t// %s ";
break;
// ** Begin sql date/time/timestamp *
case dtype_sql_date:
fmtstr = " ISC_DATE jrd_%d;\t// %s ";
break;
case dtype_sql_time:
fmtstr = " ISC_TIME jrd_%d;\t// %s ";
break;
case dtype_timestamp:
fmtstr = " ISC_TIMESTAMP jrd_%d;\t// %s ";
break;
// ** End sql date/time/timestamp *
case dtype_int64:
fmtstr = " ISC_INT64 jrd_%d;\t// %s ";
break;
case dtype_quad:
fmtstr = " ISC_QUAD jrd_%d;\t// %s ";
break;
case dtype_blob:
fmtstr = " bid jrd_%d;\t// %s ";
break;
case dtype_cstring:
case dtype_text:
fprintf(gpreGlob.out_file, " TEXT jrd_%d [%d];\t// %s ",
reference->ref_ident, field->fld_length, name);
break;
case dtype_real:
fmtstr = " float jrd_%d;\t// %s ";
break;
case dtype_double:
fmtstr = " double jrd_%d;\t// %s ";
break;
default:
{
TEXT s[ERROR_LENGTH];
fb_utils::snprintf(s, sizeof(s), "datatype %d unknown for field %s, msg %d",
field->fld_dtype, name, port->por_msg_number);
CPR_error(s);
return;
}
}
if (fmtstr)
fprintf(gpreGlob.out_file, fmtstr, reference->ref_ident, name);
}
align(column);
fprintf(gpreGlob.out_file, "} jrd_%"ULONGFORMAT";", port->por_ident);
}
//____________________________________________________________
//
// Print a fixed string at a particular column.
//
static void printa(const int column, const TEXT* string, ...)
{
va_list ptr;
va_start(ptr, string);
align(column);
vfprintf(gpreGlob.out_file, string, ptr);
va_end(ptr);
}