fis-gtm/sr_unix/op_fnfgncal.c

1012 lines
34 KiB
C
Raw Permalink Normal View History

/****************************************************************
* *
2024-07-19 11:43:27 -04:00
* Copyright 2001, 2013 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#include "gtm_string.h"
#include <stdarg.h>
#include <errno.h>
2024-07-19 11:43:27 -04:00
#ifdef GTM_PTHREAD
# include <pthread.h>
#endif
#include "gtm_stdlib.h"
#include "stringpool.h"
#include "copy.h"
#include <rtnhdr.h>
#include "stack_frame.h"
#include "op.h"
#include "lv_val.h" /* needed for "fgncal.h" */
#include "fgncal.h"
#include "gtmci.h"
#include "gtmxc_types.h"
#include "mvalconv.h"
#include "gt_timer.h"
#include "callg.h"
#include "callintogtmxfer.h"
#include "min_max.h"
#include "have_crit.h"
/******************************************************************************
*
* This routine is called by generated code with the following arguments:
*
* n argument count (int4)
* dst destination (mval *), that is, result for extrinsic function call
* null pointer means that this is a routine call
* package package name (mval *)
* extref external reference name (mval *)
* mask call-by refrence mask (int4)
* If bit=1, then call by reference,
* otherwise, call by value.
* lsb is first argument, etc.
* Implies all call-by-reference arguments must be in the first 32 arguments.
* argcnt argument count (int4). Number of input parameters.
* always equal to argument count n - 5
* arg0 the first input paramter (mval *)
* arg1 the second input parameter (mval *)
* ... ...
* argi the last input parameter, i = argcnt - 1
*
* example:
* d &mathpak.bessel(p1,p2,.p3)
*
* would result in the following call:
*
2024-07-19 11:43:27 -04:00
* op_fnfgncal(8, 0, mval*("mathpak"), mval*("bessel"), 4, 3, mval*(p1), mval*(p2), mval*(p3))
*
* where, mval*(val) indicates the address of an mval that has the string value val.
*
* If the extref has a routine name, than the extref looks like an M source
* reference. E.g.:
* s x=&$foo^bar
* entryref-->"foo.bar"*
*
*
* This routine is platform independent (at least for UNIX).
* It requires two platform specific routines:
*
* lookup package
* Return package_handle if success, NULL otherwise.
*
* void * fgn_getpak(char *package_name)
* {
* lookup package
* If not found, return NULL;
* store any system specific information in package_handle
* return package_handle;
* }
*
* lookup function
* return function address if success, NULL otherwise
*
* typedef int (*fgnfnc)();
*
* fgnfnc fgn_getrtn(void *package_handle, mstr *entry_name)
* {
* lookup entry_name
* If not found return NULL;
* return function address;
* }
*
******************************************************************************/
GBLREF stack_frame *frame_pointer;
GBLREF unsigned char *msp;
GBLREF spdesc stringpool;
GBLREF int (*callintogtm_vectortable[])();
GBLREF int mumps_status;
GBLREF volatile int4 gtmMallocDepth;
2024-07-19 11:43:27 -04:00
#ifdef GTM_PTHREAD
GBLREF boolean_t gtm_jvm_process;
GBLREF pthread_t gtm_main_thread_id;
GBLREF boolean_t gtm_main_thread_id_set;
#endif
2024-07-19 11:43:27 -04:00
error_def(ERR_JNI);
error_def(ERR_MAXSTRLEN);
error_def(ERR_TEXT);
error_def(ERR_UNIMPLOP);
error_def(ERR_XCVOIDRET);
error_def(ERR_ZCARGMSMTCH);
error_def(ERR_ZCCTENV);
error_def(ERR_ZCMAXPARAM);
error_def(ERR_ZCNOPREALLOUTPAR);
error_def(ERR_ZCRTENOTF);
error_def(ERR_ZCSTATUSRET);
error_def(ERR_ZCUSRRTN);
error_def(ERR_ZCVECTORINDX);
2024-07-19 11:43:27 -04:00
STATICDEF int call_table_initialized = 0;
/* The following are one-letter mnemonics for Java argument types (capital letters to denote output direction):
* boolean int long float double String byte[] */
STATICDEF char gtm_jtype_chars[] = { 'b', 'i', 'l', 'f', 'd', 'j', 'a',
'B', 'I', 'L', 'F', 'D', 'J', 'A' };
STATICDEF int gtm_jtype_start_idx = gtm_jboolean, /* Value of first gtm_j... type for calculation of table indices. */
gtm_jtype_count = gtm_jbyte_array - gtm_jboolean + 1; /* Number of types supported with Java call-outs. */
2024-07-19 11:43:27 -04:00
STATICFNDCL void extarg2mval(void *src, enum gtm_types typ, mval *dst, boolean_t java, boolean_t starred);
STATICFNDCL int extarg_getsize(void *src, enum gtm_types typ, mval *dst);
STATICFNDCL void op_fgnjavacal(mval *dst, mval *package, mval *extref, uint4 mask, int4 argcnt, int4 entry_argcnt,
struct extcall_package_list *package_ptr, struct extcall_entry_list *entry_ptr, va_list var);
2024-07-19 11:43:27 -04:00
/* Routine to convert external return values to mval's */
STATICFNDEF void extarg2mval(void *src, enum gtm_types typ, mval *dst, boolean_t java, boolean_t starred)
{
2024-07-19 11:43:27 -04:00
gtm_int_t s_int_num;
gtm_long_t str_len, s_long_num;
gtm_uint_t uns_int_num;
gtm_ulong_t uns_long_num;
char *cp;
struct extcall_string *sp;
2024-07-19 11:43:27 -04:00
if (java)
{
switch(typ)
{
case gtm_notfound:
break;
case gtm_void:
break;
case gtm_status:
/* Note: reason for double cast is to first turn ptr to same sized int, then big int to little int
* (on 64 bit platforms). This avoids a warning msg with newer 64 bit gcc compilers.
*/
s_int_num = (gtm_int_t)(intszofptr_t)src;
if (0 != s_int_num)
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZCSTATUSRET);
MV_FORCE_MVAL(dst, s_int_num);
break;
case gtm_jboolean:
case gtm_jint:
if (starred)
s_int_num = *((gtm_int_t *)src);
else
s_int_num = (gtm_int_t)(intszofptr_t)src;
MV_FORCE_MVAL(dst, s_int_num);
break;
case gtm_jlong:
# ifdef GTM64
if (starred)
s_long_num = *((gtm_long_t *)src);
else
s_long_num = (gtm_long_t)src;
MV_FORCE_LMVAL(dst, s_long_num);
# else
i82mval(dst, *(gtm_int64_t *)src);
# endif
break;
case gtm_jfloat:
float2mval(dst, *((float *)src));
break;
case gtm_jdouble:
double2mval(dst, *((double *)src));
break;
case gtm_jstring:
case gtm_jbyte_array:
sp = (struct extcall_string *)src;
dst->mvtype = MV_STR;
if (sp->len > MAX_STRLEN)
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_MAXSTRLEN);
dst->str.len = (mstr_len_t)sp->len;
if ((0 < sp->len) && (NULL != sp->addr))
{
dst->str.addr = sp->addr;
s2pool(&dst->str);
/* In case of GTMByteArray or GTMString, the buffer is allocated in xc_gateway.c (since the
* user might need to return a bigger buffer than the original array size will allow (in
* case of a string the content is immutable). So, if we have determined that the provided
* value buffer is legitimate (non-null and non-empty), we free it on the GT.M side. */
free(sp->addr);
}
break;
default:
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP);
break;
}
return;
}
/* The following switch is for non-Java call-outs. */
switch(typ)
{
2024-07-19 11:43:27 -04:00
case gtm_notfound:
break;
2024-07-19 11:43:27 -04:00
case gtm_void:
break;
2024-07-19 11:43:27 -04:00
case gtm_status:
/* Note: reason for double cast is to first turn ptr to same sized int, then big int to little int
2024-07-19 11:43:27 -04:00
* (on 64 bit platforms). This avoids a warning msg with newer 64 bit gcc compilers.
*/
s_int_num = (gtm_int_t)(intszofptr_t)src;
if (0 != s_int_num)
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZCSTATUSRET);
MV_FORCE_MVAL(dst, s_int_num);
break;
2024-07-19 11:43:27 -04:00
case gtm_int:
s_int_num = (gtm_int_t)(intszofptr_t)src;
MV_FORCE_MVAL(dst, s_int_num);
break;
2024-07-19 11:43:27 -04:00
case gtm_uint:
uns_int_num = (gtm_uint_t)(intszofptr_t)src;
MV_FORCE_UMVAL(dst, uns_int_num);
break;
2024-07-19 11:43:27 -04:00
case gtm_int_star:
s_int_num = *((gtm_int_t *)src);
MV_FORCE_MVAL(dst, s_int_num);
break;
2024-07-19 11:43:27 -04:00
case gtm_uint_star:
uns_int_num = *((gtm_uint_t *)src);
MV_FORCE_UMVAL(dst, uns_int_num);
break;
2024-07-19 11:43:27 -04:00
case gtm_long:
s_long_num = (gtm_long_t)src;
MV_FORCE_LMVAL(dst, s_long_num);
break;
2024-07-19 11:43:27 -04:00
case gtm_ulong:
uns_long_num = (gtm_ulong_t)src;
MV_FORCE_ULMVAL(dst, uns_long_num);
break;
2024-07-19 11:43:27 -04:00
case gtm_long_star:
s_long_num = *((gtm_long_t *)src);
MV_FORCE_LMVAL(dst, s_long_num);
break;
2024-07-19 11:43:27 -04:00
case gtm_ulong_star:
uns_long_num = *((gtm_ulong_t *)src);
MV_FORCE_ULMVAL(dst, uns_long_num);
break;
2024-07-19 11:43:27 -04:00
case gtm_string_star:
sp = (struct extcall_string *)src;
dst->mvtype = MV_STR;
if (sp->len > MAX_STRLEN)
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_MAXSTRLEN);
dst->str.len = (mstr_len_t)sp->len;
if ((0 < sp->len) && (NULL != sp->addr))
{
dst->str.addr = sp->addr;
s2pool(&dst->str);
}
break;
2024-07-19 11:43:27 -04:00
case gtm_float_star:
float2mval(dst, *((float *)src));
break;
2024-07-19 11:43:27 -04:00
case gtm_char_star:
cp = (char *)src;
assert(((INTPTR_T)cp < (INTPTR_T)stringpool.base) || ((INTPTR_T)cp > (INTPTR_T)stringpool.top));
dst->mvtype = MV_STR;
str_len = STRLEN(cp);
if (str_len > MAX_STRLEN)
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_MAXSTRLEN);
dst->str.len = (mstr_len_t)str_len;
dst->str.addr = cp;
s2pool(&dst->str);
break;
2024-07-19 11:43:27 -04:00
case gtm_char_starstar:
if (!src)
dst->mvtype = 0;
else
2024-07-19 11:43:27 -04:00
extarg2mval(*((char **)src), gtm_char_star, dst, java, starred);
break;
2024-07-19 11:43:27 -04:00
case gtm_double_star:
double2mval(dst, *((double *)src));
break;
default:
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP);
break;
}
return;
}
2024-07-19 11:43:27 -04:00
/* Subroutine to calculate stringpool requirements for an external argument */
STATICFNDEF int extarg_getsize(void *src, enum gtm_types typ, mval *dst)
{
char *cp, **cpp;
struct extcall_string *sp;
if (!src)
return 0;
switch(typ)
2024-07-19 11:43:27 -04:00
{ /* The following group of cases either return nothing or use the numeric part of the mval */
case gtm_notfound:
case gtm_void:
case gtm_double_star:
case gtm_status:
case gtm_int:
case gtm_uint:
case gtm_long:
case gtm_ulong:
case gtm_float_star:
case gtm_int_star:
case gtm_uint_star:
case gtm_long_star:
case gtm_ulong_star:
case gtm_jboolean:
case gtm_jint:
case gtm_jlong:
case gtm_jfloat:
case gtm_jdouble:
return 0;
2024-07-19 11:43:27 -04:00
case gtm_char_starstar:
cpp = (char **)src;
if (*cpp)
return STRLEN(*cpp);
else
return 0;
2024-07-19 11:43:27 -04:00
case gtm_char_star:
cp = (char *)src;
return STRLEN(cp);
2024-07-19 11:43:27 -04:00
case gtm_jstring:
case gtm_jbyte_array:
case gtm_string_star:
sp = (struct extcall_string *)src;
if ((0 < sp->len)
&& ((INTPTR_T)sp->addr < (INTPTR_T)stringpool.free)
&& ((INTPTR_T)sp->addr >= (INTPTR_T)stringpool.base))
2024-07-19 11:43:27 -04:00
{ /* The stuff is already in the stringpool */
assert(dst->str.addr == sp->addr);
dst->str.addr = sp->addr;
2024-07-19 11:43:27 -04:00
sp->addr = NULL; /* Prevent subsequent s2pool */
return 0;
} else if (NULL == sp->addr)
sp->len = 0;
return (int)(sp->len);
break;
default:
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP);
break;
}
return 0; /* This should never get executed, added to make compiler happy */
}
2024-07-19 11:43:27 -04:00
STATICFNDEF void op_fgnjavacal(mval *dst, mval *package, mval *extref, uint4 mask, int4 argcnt, int4 entry_argcnt,
struct extcall_package_list *package_ptr, struct extcall_entry_list *entry_ptr, va_list var)
{
boolean_t error_in_xc = FALSE;
char *free_string_pointer, *free_string_pointer_start, jtype_char;
char str_buffer[MAX_NAME_LENGTH], *tmp_buff_ptr, *jni_err_buf;
char *types_descr_ptr, *types_descr_dptr, *xtrnl_table_name;
gparam_list *param_list;
gtm_long_t *free_space_pointer;
int i, j, save_mumps_status;
int4 m1, m2, n;
INTPTR_T status;
mval *v;
va_list var_copy;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
# ifdef GTM_PTHREAD
assert(gtm_jvm_process == gtm_main_thread_id_set);
gtm_jvm_process = TRUE;
if (!gtm_main_thread_id_set)
{
gtm_main_thread_id = pthread_self();
gtm_main_thread_id_set = TRUE;
}
# endif
/* This is how many parameters we will use for callg, including the implicit ones described below. So, better make
* sure we are not trying to pass more than callg can handle.
*/
if (MAX_ACTUALS < argcnt + 3)
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZCMAXPARAM);
VAR_COPY(var_copy, var);
/* Compute size of parameter block */
n = entry_ptr->parmblk_size + (3 * SIZEOF(void *)); /* This is enough for the parameters and the fixed length entries */
mask >>= 2; /* Bypass the class and method arguments. */
/* The first byte of the first argument (types_descr_ptr) stores the number of expected arguments, according to the external
* calls table; the second byte describes the expected return type, and subsequent bytes---the expected argument types using
* a one-character mnemonic which gets deciphered in the JNI layer. Note that in case of an error the xc_gateway.c module
* would set the first byte to 0xFF, followed by an address at which the error message is stored. For that reason, we
* allocate more space than we might strictly need for the arguments, return type, and type descriptor.
*/
types_descr_ptr = (char *)malloc(MAX(SIZEOF(char) * (argcnt + 2), SIZEOF(char *) * 2));
types_descr_dptr = types_descr_ptr;
*types_descr_dptr = (char)entry_argcnt;
types_descr_dptr++;
if (dst)
{ /* Record the expected return type. */
switch (entry_ptr->return_type)
{
case gtm_status:
*types_descr_dptr = 'i';
break;
case gtm_jlong:
*types_descr_dptr = 'l';
break;
default:
*types_descr_dptr = 'v';
}
} else
*types_descr_dptr = 'v';
types_descr_dptr++;
assert(2 * gtm_jtype_count == SIZEOF(gtm_jtype_chars));
for (i = argcnt + 2, j = -2, m1 = entry_ptr->input_mask, m2 = mask & entry_ptr->output_mask; 0 < i; i--, j++)
{ /* Enforce mval values and record expected argument types. */
v = va_arg(var, mval *);
if (0 > j)
{
MV_FORCE_STR(v);
n += v->str.len + SIZEOF(gtm_long_t) + 1;
continue;
}
if (m1 & 1)
{
if ((gtm_jstring == entry_ptr->parms[j]) || (gtm_jbyte_array == entry_ptr->parms[j]))
{
MV_FORCE_STR(v);
n += v->str.len + SIZEOF(gtm_long_t) + 1;
} else
{
MV_FORCE_DEFINED(v);
}
}
jtype_char = entry_ptr->parms[j] - gtm_jtype_start_idx;
if ((0 > jtype_char) || (gtm_jtype_count <= jtype_char))
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP);
else
*types_descr_dptr = gtm_jtype_chars[(m2 & 1) ? (gtm_jtype_count + jtype_char) : jtype_char];
types_descr_dptr++;
m1 >>= 1;
m2 >>= 1;
}
va_end(var);
/* Double the size, to take care of any alignments in the middle. */
param_list = (gparam_list *)malloc(n * 2);
param_list->arg[0] = (void *)types_descr_ptr;
/* Adding 3 to account for type descriptions, class name, and method name arguments. */
free_space_pointer = (gtm_long_t *)((char *)param_list + SIZEOF(intszofptr_t) + (SIZEOF(void *) * (argcnt + 3)));
/* Adding 3 for the same reason as above and another 3 to account for the fact that each of type description, class name,
* and method name arguments require room in the free_space buffer, which comes ahead of free_string buffer in memory.
*/
free_string_pointer_start = free_string_pointer = (char *)param_list + entry_ptr->parmblk_size + (SIZEOF(void *) * (3 + 3));
/* Load-up the parameter list */
VAR_COPY(var, var_copy);
/* We need to enter this loop even if argcnt == 0, so that the class and method arguments get set. */
for (i = (0 == argcnt ? -1 : 0), j = 1, m1 = entry_ptr->input_mask, m2 = mask & entry_ptr->output_mask; i < argcnt; j++)
{
v = va_arg(var_copy, mval *);
if (j < 3)
{
param_list->arg[j] = free_string_pointer;
if (v->str.len)
memcpy(free_string_pointer, v->str.addr, v->str.len);
free_string_pointer += v->str.len;
*free_string_pointer++ = 0;
/* In case there are 0 arguments. */
if (2 == j && 0 > i)
i = 0;
continue;
}
/* Verify that all input values are defined. */
switch (entry_ptr->parms[i])
{
case gtm_jboolean:
if (m2 & 1)
{ /* Output expected. */
param_list->arg[j] = free_space_pointer;
*((gtm_int_t *)free_space_pointer) = (m1 & 1) ? ((gtm_int_t)(mval2i(v) ? 1 : 0)) : 0;
free_space_pointer++;
} else if (m1 & 1)
param_list->arg[j] = (void *)((gtm_long_t)(mval2i(v) ? 1 : 0));
break;
case gtm_jint:
if (m2 & 1)
{ /* Output expected. */
param_list->arg[j] = free_space_pointer;
*((gtm_int_t *)free_space_pointer) = (m1 & 1) ? (gtm_int_t)mval2i(v) : 0;
free_space_pointer++;
} else if (m1 & 1)
param_list->arg[j] = (void *)(gtm_long_t)mval2i(v);
break;
case gtm_jlong:
if (m2 & 1)
{ /* Output expected. */
# ifndef GTM64
free_space_pointer = (gtm_long_t *)(ROUND_UP2(((INTPTR_T)free_space_pointer),
SIZEOF(gtm_int64_t)));
# endif
param_list->arg[j] = free_space_pointer;
*((gtm_int64_t *)free_space_pointer) = (m1 & 1) ? (gtm_int64_t)mval2i8(v) : 0;
free_space_pointer = (gtm_long_t *)((char *)free_space_pointer + SIZEOF(gtm_int64_t));
} else if (m1 & 1)
{
# ifdef GTM64
param_list->arg[j] = (void *)(gtm_int64_t)mval2i8(v);
# else
/* Only need to do this rounding on non-64 it platforms because this one type has a 64 bit
* alignment requirement on those platforms.
*/
free_space_pointer = (gtm_long_t *)(ROUND_UP2(((INTPTR_T)free_space_pointer),
SIZEOF(gtm_int64_t)));
param_list->arg[j] = free_space_pointer;
*((gtm_int64_t *)free_space_pointer) = (gtm_int64_t)mval2i8(v);
free_space_pointer = (gtm_long_t *)((char *)free_space_pointer + SIZEOF(gtm_int64_t));
# endif
}
break;
case gtm_jfloat:
param_list->arg[j] = free_space_pointer;
*((float *)free_space_pointer) = (m1 & 1) ? (float)mval2double(v) : (float)0.0;
free_space_pointer++;
break;
case gtm_jdouble:
# ifndef GTM64
/* Only need to do this rounding on non-64 it platforms because this one type has a 64 bit
* alignment requirement on those platforms.
*/
free_space_pointer = (gtm_long_t *)(ROUND_UP2(((INTPTR_T)free_space_pointer), SIZEOF(double)));
# endif
param_list->arg[j] = free_space_pointer;
*((double *)free_space_pointer) = (m1 & 1) ? (double)mval2double(v) : (double)0.0;
free_space_pointer = (gtm_long_t *)((char *)free_space_pointer + SIZEOF(double));
break;
case gtm_jstring:
case gtm_jbyte_array:
param_list->arg[j] = free_space_pointer;
*free_space_pointer++ = (gtm_long_t)v->str.len;
*(char **)free_space_pointer = (char *)free_string_pointer;
free_space_pointer++;
if (m1 & 1)
{
if (v->str.len)
memcpy(free_string_pointer, v->str.addr, v->str.len);
free_string_pointer += v->str.len;
*free_string_pointer++ = 0;
}
break;
default:
va_end(var_copy);
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP);
break;
}
assert(((char *)free_string_pointer <= ((char *)param_list + n * 2))
&& ((char *)free_space_pointer <= ((char *)param_list + n * 2)));
i++;
m1 = m1 >> 1;
m2 = m2 >> 1;
}
assert((char *)free_space_pointer <= free_string_pointer_start);
va_end(var_copy);
param_list->n = argcnt + 3; /* Take care of the three implicit parameters. */
save_mumps_status = mumps_status; /* Save mumps_status as a callin from external call may change it. */
status = callg((callgfnptr)entry_ptr->fcn, param_list);
mumps_status = save_mumps_status;
/* The first byte of the type description argument gets set to 0xFF in case error happened in JNI glue code,
* so check for that and act accordingly.
*/
if ((char)0xFF == *(char *)param_list->arg[0])
{
error_in_xc = TRUE;
jni_err_buf = *(char **)((char *)param_list->arg[0] + SIZEOF(char *));
if (NULL != jni_err_buf)
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_JNI, 2, LEN_AND_STR(jni_err_buf));
else
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZCSTATUSRET);
}
free(types_descr_ptr);
/* Exit from the residual call-in environment(SFF_CI and base frames) which might still exist on M stack when the externally
* called function in turn called into an M routine.
*/
if (frame_pointer->flags & SFF_CI)
ci_ret_code_quit();
/* Only process the input-output and output-only arguments if the external call succeeded; otherwise, return -1
* if non-void return is expected. */
if (!error_in_xc)
{ /* Compute space requirement for return values. */
n = 0;
VAR_COPY(var_copy, var);
for (i = 0, j = 1, m1 = mask & entry_ptr->output_mask; i < argcnt; j++)
{
v = va_arg(var, mval *);
if (j < 3)
continue;
if (m1 & 1)
n += extarg_getsize(param_list->arg[j], entry_ptr->parms[i], v);
i++;
m1 = m1 >> 1;
}
va_end(var);
if (dst)
n += extarg_getsize((void *)&status, gtm_status, dst);
ENSURE_STP_FREE_SPACE(n);
/* Convert return values. */
VAR_COPY(var, var_copy);
for (i = 0, j = 1, m1 = mask & entry_ptr->output_mask; i < argcnt; j++)
{
v = va_arg(var_copy, mval *);
if (j < 3)
continue;
if (m1 & 1)
extarg2mval((void *)param_list->arg[j], entry_ptr->parms[i], v, TRUE, TRUE);
i++;
m1 = m1 >> 1;
}
va_end(var);
if (dst)
{
if (entry_ptr->return_type != gtm_void)
extarg2mval((void *)status, entry_ptr->return_type, dst, TRUE, FALSE);
else
{
memcpy(str_buffer, PACKAGE_ENV_PREFIX, SIZEOF(PACKAGE_ENV_PREFIX));
tmp_buff_ptr = &str_buffer[SIZEOF(PACKAGE_ENV_PREFIX) - 1];
if (package->str.len)
{
assert(package->str.len < MAX_NAME_LENGTH - SIZEOF(PACKAGE_ENV_PREFIX) - 1);
*tmp_buff_ptr++ = '_';
memcpy(tmp_buff_ptr, package->str.addr, package->str.len);
tmp_buff_ptr += package->str.len;
}
*tmp_buff_ptr = 0;
xtrnl_table_name = GETENV(str_buffer);
if (NULL == xtrnl_table_name)
{ /* Environment variable for the package not found. This part of code is for more safety.
* We should not come into this path at all.
*/
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ZCCTENV, 2, LEN_AND_STR(str_buffer));
}
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_XCVOIDRET, 4,
LEN_AND_STR(entry_ptr->call_name.addr), LEN_AND_STR(xtrnl_table_name));
}
}
} else if (dst && (gtm_void != entry_ptr->return_type))
i2mval(dst, -1);
free(param_list);
check_for_timer_pops();
return;
}
void op_fnfgncal (uint4 n_mvals, mval *dst, mval *package, mval *extref, uint4 mask, int4 argcnt, ...)
{
2024-07-19 11:43:27 -04:00
boolean_t java = FALSE;
char *free_string_pointer, *free_string_pointer_start;
char str_buffer[MAX_NAME_LENGTH], *tmp_buff_ptr, *xtrnl_table_name;
int i, pre_alloc_size, rslt, save_mumps_status;
int4 callintogtm_vectorindex, n;
gparam_list *param_list;
gtm_long_t *free_space_pointer;
INTPTR_T status;
2024-07-19 11:43:27 -04:00
mval *v;
struct extcall_package_list *package_ptr;
struct extcall_entry_list *entry_ptr;
2024-07-19 11:43:27 -04:00
uint4 m1;
va_list var;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
assert(n_mvals == argcnt + 5);
2024-07-19 11:43:27 -04:00
assert(MV_IS_STRING(package)); /* Package and routine are literal strings */
assert(MV_IS_STRING(extref));
if (MAX_ACTUALS < argcnt)
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZCMAXPARAM);
assert(INTRPT_OK_TO_INTERRUPT == intrpt_ok_state); /* Interrupts should be enabled for external calls */
/* Find package */
for (package_ptr = TREF(extcall_package_root); package_ptr; package_ptr = package_ptr->next_package)
{
MSTR_CMP(package_ptr->package_name, package->str, rslt);
if (0 == rslt)
break;
}
/* If package has not been found, create it */
if (NULL == package_ptr)
{
package_ptr = exttab_parse(package);
package_ptr->next_package = TREF(extcall_package_root);
TREF(extcall_package_root) = package_ptr;
}
/* At this point, we have a valid package, pointed to by package_ptr */
assert(NULL != package_ptr);
if (NULL == package_ptr->package_handle)
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno);
/* Find entry */
for (entry_ptr = package_ptr->first_entry; entry_ptr; entry_ptr = entry_ptr->next_entry)
{
MSTR_CMP(entry_ptr->entry_name, extref->str, rslt);
if (0 == rslt)
break;
}
if (call_table_initialized == FALSE)
{
call_table_initialized = TRUE;
init_callin_functable();
}
2024-07-19 11:43:27 -04:00
/* Entry not found */
if ((NULL == entry_ptr) || (NULL == entry_ptr->fcn))
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ZCRTENOTF, 2, extref->str.len, extref->str.addr);
/* Detect a call-out to Java. */
if ((NULL != entry_ptr->call_name.addr) && !strncmp(entry_ptr->call_name.addr, "gtm_xcj", 7))
{
java = TRUE;
argcnt -= 2;
}
/* It is an error to have more actual parameters than formal parameters */
if (argcnt > entry_ptr->argcnt)
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ZCARGMSMTCH, 2, argcnt, entry_ptr->argcnt);
VAR_START(var, argcnt);
2024-07-19 11:43:27 -04:00
if (java)
{
op_fgnjavacal(dst, package, extref, mask, argcnt, entry_ptr->argcnt, package_ptr, entry_ptr, var);
return;
}
/* Compute size of parameter block */
n = entry_ptr->parmblk_size; /* This is enough for the parameters and the fixed length entries */
/* Now, add enough for the char *'s and the char **'s and string *'s */
for (i = 0, m1 = entry_ptr->input_mask; i < argcnt; i++, m1 = m1 >> 1)
{
v = va_arg(var, mval *);
2024-07-19 11:43:27 -04:00
/* If it is an input value of char* or char **, add the length. Also a good time to force it into string form */
switch(entry_ptr->parms[i])
{
2024-07-19 11:43:27 -04:00
case gtm_char_star:
n += (-1 != entry_ptr->param_pre_alloc_size[i]) ? entry_ptr->param_pre_alloc_size[i] : 0;
/* Caution fall through */
2024-07-19 11:43:27 -04:00
case gtm_char_starstar:
if (m1 & 1)
{
MV_FORCE_STR(v);
n += v->str.len + 1;
}
break;
2024-07-19 11:43:27 -04:00
case gtm_string_star:
if (m1 & 1)
{
MV_FORCE_STR(v);
n += v->str.len + 1;
} else
n += (-1 != entry_ptr->param_pre_alloc_size[i]) ? entry_ptr->param_pre_alloc_size[i] : 0;
break;
2024-07-19 11:43:27 -04:00
case gtm_double_star:
n += SIZEOF(double);
if (m1 & 1)
MV_FORCE_DEFINED(v);
break;
default:
if (m1 & 1)
MV_FORCE_DEFINED(v);
break;
}
}
va_end(var);
/* Double the size, to take care of any alignments in the middle */
param_list = (gparam_list *)malloc(n * 2);
2024-07-19 11:43:27 -04:00
free_space_pointer = (gtm_long_t *)((char *)param_list + SIZEOF(intszofptr_t) + (SIZEOF(void *) * argcnt));
free_string_pointer_start = free_string_pointer = (char *)param_list + entry_ptr->parmblk_size;
2024-07-19 11:43:27 -04:00
/* Load-up the parameter list */
VAR_START(var, argcnt);
for (i = 0, m1 = entry_ptr->input_mask; i < argcnt; i++, m1 = m1 >> 1)
{
v = va_arg(var, mval *);
/* Note that even in this second pass at these mvals, we need to do the MV_FORCE processing because
2024-07-19 11:43:27 -04:00
* in a NOUNDEF environment, undefiend mvals were NOT modified in the first pass and thus reamin undefined
* in this pass.
*/
if (gtm_char_star != entry_ptr->parms[i] && (m1 & 1))
MV_FORCE_DEFINED(v); /* Redefine undef'd mvals */
/* Verify that all input values are defined */
pre_alloc_size = entry_ptr->param_pre_alloc_size[i];
switch(entry_ptr->parms[i])
{ /* Note the int/long types are handled separately here in anticipation of correct handling
2024-07-19 11:43:27 -04:00
* of "long" types on 64 bit hardware in the future. For the time being however, they are
* using the same mval2i interface routine so are both restricted to 32 bits.
*/
case gtm_uint:
if (m1 & 1)
2024-07-19 11:43:27 -04:00
param_list->arg[i] = (void *)(gtm_long_t)mval2ui(v);
/* Note: output gtm_int and gtm_uint is an error (only "star" flavor can be modified) */
break;
2024-07-19 11:43:27 -04:00
case gtm_int:
if (m1 & 1)
2024-07-19 11:43:27 -04:00
param_list->arg[i] = (void *)(gtm_long_t)mval2i(v);
break;
2024-07-19 11:43:27 -04:00
case gtm_ulong:
if (m1 & 1)
2024-07-19 11:43:27 -04:00
{
GTM64_ONLY(param_list->arg[i] = (void *)(gtm_uint64_t)mval2ui8(v));
NON_GTM64_ONLY(param_list->arg[i] = (void *)(gtm_ulong_t)mval2ui(v));
}
/* Note: output xc_long and xc_ulong is an error as described above */
break;
2024-07-19 11:43:27 -04:00
case gtm_long:
if (m1 & 1)
2024-07-19 11:43:27 -04:00
{
GTM64_ONLY(param_list->arg[i] = (void *)(gtm_int64_t)mval2i8(v));
NON_GTM64_ONLY(param_list->arg[i] = (void *)(gtm_long_t)mval2i(v));
}
break;
2024-07-19 11:43:27 -04:00
case gtm_char_star:
param_list->arg[i] = free_string_pointer;
if (m1 & 1)
{
if (v->str.len)
memcpy(free_string_pointer, v->str.addr, v->str.len);
free_string_pointer += v->str.len;
*free_string_pointer++ = 0;
} else if (-1 != pre_alloc_size)
free_string_pointer += pre_alloc_size;
else /* Output and no pre-allocation specified */
{
if (0 == package->str.len)
2024-07-19 11:43:27 -04:00
/* Default package - do not display package name */
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_ZCNOPREALLOUTPAR, 5, i + 1,
RTS_ERROR_LITERAL("<DEFAULT>"), extref->str.len, extref->str.addr);
else
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_ZCNOPREALLOUTPAR, 5, i + 1,
package->str.len, package->str.addr, extref->str.len, extref->str.addr);
}
break;
2024-07-19 11:43:27 -04:00
case gtm_char_starstar:
param_list->arg[i] = free_space_pointer;
if (m1 & 1)
{
*(char **)free_space_pointer = free_string_pointer;
if (v->str.len)
memcpy(free_string_pointer, v->str.addr, v->str.len);
free_string_pointer += v->str.len;
*free_string_pointer++ = 0;
} else
*(char **)free_space_pointer = free_string_pointer++;
free_space_pointer++;
break;
2024-07-19 11:43:27 -04:00
case gtm_int_star:
param_list->arg[i] = free_space_pointer;
2024-07-19 11:43:27 -04:00
*((gtm_int_t *)free_space_pointer) = (m1 & 1) ? (gtm_int_t)mval2i(v) : 0;
free_space_pointer++;
break;
2024-07-19 11:43:27 -04:00
case gtm_uint_star:
param_list->arg[i] = free_space_pointer;
2024-07-19 11:43:27 -04:00
*((gtm_uint_t *)free_space_pointer) = (m1 & 1) ? (gtm_uint_t)mval2ui(v) : 0;
free_space_pointer++;
break;
2024-07-19 11:43:27 -04:00
case gtm_long_star:
param_list->arg[i] = free_space_pointer;
2024-07-19 11:43:27 -04:00
GTM64_ONLY(*((gtm_int64_t *)free_space_pointer) = (m1 & 1) ? (gtm_int64_t)mval2i8(v) : 0);
NON_GTM64_ONLY(*((gtm_long_t *)free_space_pointer) = (m1 & 1) ? (gtm_long_t)mval2i(v) : 0);
free_space_pointer++;
break;
2024-07-19 11:43:27 -04:00
case gtm_ulong_star:
param_list->arg[i] = free_space_pointer;
2024-07-19 11:43:27 -04:00
GTM64_ONLY(*((gtm_uint64_t *)free_space_pointer) = (m1 & 1) ? (gtm_uint64_t)mval2ui8(v) : 0);
NON_GTM64_ONLY(*((gtm_ulong_t *)free_space_pointer) = (m1 & 1) ? (gtm_ulong_t)mval2ui(v) : 0);
free_space_pointer++;
break;
2024-07-19 11:43:27 -04:00
case gtm_string_star:
param_list->arg[i] = free_space_pointer;
2024-07-19 11:43:27 -04:00
*free_space_pointer++ = (gtm_long_t)v->str.len;
*(char **)free_space_pointer = (char *)free_string_pointer;
free_space_pointer++;
if (m1 & 1)
{
if (v->str.len)
memcpy(free_string_pointer, v->str.addr, v->str.len);
free_string_pointer += v->str.len;
} else if (-1 != pre_alloc_size)
free_string_pointer += pre_alloc_size;
else /* Output and no pre-allocation specified */
{
if (0 == package->str.len)
2024-07-19 11:43:27 -04:00
/* Default package - do not display package name */
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_ZCNOPREALLOUTPAR, 5, i + 1,
RTS_ERROR_LITERAL("<DEFAULT>"),
extref->str.len, extref->str.addr);
else
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_ZCNOPREALLOUTPAR, 5, i + 1,
package->str.len, package->str.addr,
extref->str.len, extref->str.addr);
}
break;
2024-07-19 11:43:27 -04:00
case gtm_float_star:
param_list->arg[i] = free_space_pointer;
*((float *)free_space_pointer) = (m1 & 1) ? (float)mval2double(v) : (float)0.0;
free_space_pointer++;
break;
2024-07-19 11:43:27 -04:00
case gtm_double_star:
/* Only need to do this rounding on non-64 it platforms because this one type has a 64 bit
2024-07-19 11:43:27 -04:00
* alignment requirement on those platforms.
*/
NON_GTM64_ONLY(free_space_pointer = (gtm_long_t *)(ROUND_UP2(((INTPTR_T)free_space_pointer),
SIZEOF(double))));
param_list->arg[i] = free_space_pointer;
*((double *)free_space_pointer) = (m1 & 1) ? (double)mval2double(v) : (double)0.0;
2024-07-19 11:43:27 -04:00
free_space_pointer = (gtm_long_t *)((char *)free_space_pointer + SIZEOF(double));
break;
2024-07-19 11:43:27 -04:00
case gtm_pointertofunc:
if (((callintogtm_vectorindex = (int4)mval2i(v)) >= gtmfunc_unknown_function)
|| (callintogtm_vectorindex < 0))
{
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(7) ERR_ZCVECTORINDX, 1, callintogtm_vectorindex,
ERR_TEXT, 2, RTS_ERROR_TEXT("Passing Null vector"));
param_list->arg[i] = 0;
} else
param_list->arg[i] = (void *)callintogtm_vectortable[callintogtm_vectorindex];
break;
2024-07-19 11:43:27 -04:00
case gtm_pointertofunc_star:
/* Cannot pass in a function address to be modified by the user program */
free_space_pointer = (gtm_long_t *)ROUND_UP2(((INTPTR_T)free_space_pointer), SIZEOF(INTPTR_T));
param_list->arg[i] = free_space_pointer;
*((INTPTR_T *)free_space_pointer) = 0;
free_space_pointer++;
break;
default:
va_end(var);
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP);
break;
}
}
assert((char *)free_space_pointer <= free_string_pointer_start);
va_end(var);
param_list->n = argcnt;
2024-07-19 11:43:27 -04:00
save_mumps_status = mumps_status; /* Save mumps_status as a callin from external call may change it */
status = callg((callgfnptr)entry_ptr->fcn, param_list);
mumps_status = save_mumps_status;
/* Exit from the residual call-in environment(SFF_CI and base frames) which might
* still exist on M stack when the externally called function in turn called
2024-07-19 11:43:27 -04:00
* into an M routine.
*/
if (frame_pointer->flags & SFF_CI)
ci_ret_code_quit();
/* NOTE: ADD RETURN STATUS CALCUATIONS HERE */
2024-07-19 11:43:27 -04:00
/* Compute space requirement for return values */
n = 0;
VAR_START(var, argcnt);
for (i = 0, m1 = mask & entry_ptr->output_mask; i < argcnt; i++, m1 = m1 >> 1)
{
v = va_arg(var, mval *);
if (m1 & 1)
n += extarg_getsize(param_list->arg[i], entry_ptr->parms[i], v);
}
va_end(var);
if (dst)
2024-07-19 11:43:27 -04:00
n += extarg_getsize((void *)&status, gtm_status, dst);
ENSURE_STP_FREE_SPACE(n);
2024-07-19 11:43:27 -04:00
/* Convert return values */
VAR_START(var, argcnt);
for (i = 0, m1 = mask & entry_ptr->output_mask; i < argcnt; i++, m1 = m1 >> 1)
{
v = va_arg(var, mval *);
if (m1 & 1)
2024-07-19 11:43:27 -04:00
extarg2mval((void *)param_list->arg[i], entry_ptr->parms[i], v, FALSE, TRUE);
}
va_end(var);
if (dst)
{
2024-07-19 11:43:27 -04:00
if (entry_ptr->return_type != gtm_void)
extarg2mval((void *)status, entry_ptr->return_type, dst, FALSE, FALSE);
else
{
memcpy(str_buffer, PACKAGE_ENV_PREFIX, SIZEOF(PACKAGE_ENV_PREFIX));
tmp_buff_ptr = &str_buffer[SIZEOF(PACKAGE_ENV_PREFIX) - 1];
if (package->str.len)
{
assert(package->str.len < MAX_NAME_LENGTH - SIZEOF(PACKAGE_ENV_PREFIX) - 1);
*tmp_buff_ptr++ = '_';
memcpy(tmp_buff_ptr, package->str.addr, package->str.len);
tmp_buff_ptr += package->str.len;
}
*tmp_buff_ptr = 0;
xtrnl_table_name = GETENV(str_buffer);
if (NULL == xtrnl_table_name)
{
/* Environment variable for the package not found.
* This part of code is for more safety. We should
* not come into this path at all.
*/
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_ZCCTENV, 2, LEN_AND_STR(str_buffer));
}
2024-07-19 11:43:27 -04:00
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_XCVOIDRET, 4,
LEN_AND_STR(entry_ptr->call_name.addr), LEN_AND_STR(xtrnl_table_name));
}
}
free(param_list);
check_for_timer_pops();
return;
}