/**************************************************************************** ** ** This file is part of GAP, a system for computational discrete algebra. ** ** Copyright of GAP belongs to its developers, whose names are too numerous ** to list here. Please refer to the COPYRIGHT file for details. ** ** SPDX-License-Identifier: GPL-2.0-or-later ** ** This file contains the functions of the global variables package. ** ** The global variables package is the part of the kernel that manages ** global variables, i.e., the global namespace. A global variable binds an ** identifier to a value. ** ** A global variable can be automatic. That means that the global variable ** binds the identifier to a function and an argument. When the value of ** the global variable is needed, the function is called with the argument. ** This function call should, as a side-effect, execute an assignment of a ** value to the global variable, otherwise an error is signalled. ** ** A global variable can have a number of internal copies, i.e., C variables ** that always reference the same value as the global variable. ** It can also have a special type of internal copy (a fopy) only used for ** functions, where the internal copies ** only reference the same value as the global variable if it is a function. ** Otherwise the internal copies reference functions that signal an error.
*/
/**************************************************************************** ** *V ValGVars . . . . . . . . . . . . . . . . . . values of global variables *V PtrGVars . . . . . . . . . . . . . pointer to values of global variables **
*/ #ifdef USE_GVAR_BUCKETS /* ** 'ValGVars' references the bags containing the values of the global ** variables. ** ** 'PtrGVars' is a pointer to the 'ValGVars' bag+1. This makes it faster to ** access global variables.
*/ static Obj ValGVars[GVAR_BUCKETS]; static Obj * PtrGVars[GVAR_BUCKETS]; #else /* ** 'ValGVars' is the bag containing the values of the global variables. ** ** 'PtrGVars' is a pointer to the 'ValGVars' bag. This makes it faster to ** access global variables. ** ** Since a garbage collection may move this bag around, the pointer ** 'PtrGVars' must be recalculated afterwards. This is done in function ** 'GVarsAfterCollectBags' which is called by 'VarsAfterCollectBags'.
*/ static Obj ValGVars; static Obj * PtrGVars; #endif
// FIXME/TODO: Do we still need the VAL_GVAR_INTERN macro, or can we replace // it by ValGVar everywhere? The difference is of course the memory barrier, // which might cause a performance penalty (OTOH, not using it right now might // or might not be a bug?!?) #define VAL_GVAR_INTERN(gvar) (PtrGVars[GVAR_BUCKET(gvar)] \
[GVAR_INDEX(gvar)-1])
typedefstruct { // 'gvarWriteFlag' is a value of type GVarWriteFlag which denotes whether // the variable is assignable, read-only, or constant. unsignedchar gvarWriteFlag : 2;
// 'hasExprCopiesFopies' indicates whether the variable has ever had a // non-default value assigned to ExprGVars, CopiesGVars or FopiesGVars. // Note that this value is never cleared at present, so it can be set to 1 // while these three arrays all have their default value, but if it is 0 // these arrays definitely have their default values. unsignedchar hasExprCopiesFopies : 1;
// 'isDeclared' indicates whether the variable was marked by // 'DeclareGlobalName' in which case no "Unbound global variable" syntax // warnings should be issued for it. unsignedchar isDeclared : 1;
} GVarFlagInfo;
// If this size increases, the type used in GetGVarFlags and // SetGVarFlags below must be changed
GAP_STATIC_ASSERT(sizeof(GVarFlagInfo) == sizeof(unsignedchar), "GVarFlagInfo size mismatch");
static GVarFlagInfo GetGVarFlagInfo(Int gvar)
{ unsignedchar val = INT_INTOBJ(ELM_GVAR_LIST(FlagsGVars, gvar));
GVarFlagInfo info; // This is technically the safest way of converting a struct to an integer // and is optimised away by the compiler
memcpy(&info, &val, sizeof(GVarFlagInfo)); return info;
}
staticvoid SetGVarFlagInfo(Int gvar, GVarFlagInfo info)
{ unsignedchar val; // This is technically the safest way of converting an integer into a // struct and is optimised away by the compiler
memcpy(&val, &info, sizeof(GVarFlagInfo));
SET_ELM_GVAR_LIST(FlagsGVars, gvar, INTOBJ_INT(val));
}
staticvoid InitGVarFlagInfo(Int gvar)
{ // This is equal to setting all members of GVarFlagInfo to 0
SET_ELM_GVAR_LIST(FlagsGVars, gvar, INTOBJ_INT(0));
}
// Helper functions to more easily set members of GVarFlagInfo staticvoid SetGVarWriteState(Int gvar, GVarWriteFlag w)
{
GVarFlagInfo info = GetGVarFlagInfo(gvar);
info.gvarWriteFlag = w;
SetGVarFlagInfo(gvar, info);
}
/**************************************************************************** ** *V ErrorMustEvalToFuncFunc . . . . . . . . . function that signals an error *F ErrorMustEvalToFuncHandler(<self>,<args>) . handler that signals an error ** ** 'ErrorMustEvalToFuncFunc' is a (variable number of args) function that ** signals the error ``Function: <func> be a function''. ** ** 'ErrorMustEvalToFuncHandler' is the handler that signals the error ** ``Function: <func> must be a function''.
*/
Obj ErrorMustEvalToFuncFunc;
static Obj ErrorMustEvalToFuncHandler(Obj self, Obj args)
{
ErrorQuit("Function Calls: must be a function", 0, 0); return 0;
}
/**************************************************************************** ** *V ErrorMustHaveAssObjFunc . . . . . . . . . function that signals an error *F ErrorMustHaveAssObjHandler(<self>,<args>) . handler that signals an error ** ** 'ErrorMustHaveAssObjFunc' is a (variable number of args) function that ** signals the error ``Variable: <<unknown>> must have an assigned value''. ** ** 'ErrorMustHaveAssObjHandler' is the handler that signals the error ** ``Variable: <<unknown>> must have an assigned value''.
*/
Obj ErrorMustHaveAssObjFunc;
static Obj ErrorMustHaveAssObjHandler(Obj self, Obj args)
{
ErrorQuit("Variable: <> must have an assigned value", 0, 0); return 0;
}
/**************************************************************************** ** *F AssGVar(<gvar>,<val>) . . . . . . . . . . . . assign to a global variable ** ** 'AssGVar' assigns the value <val> to the global variable <gvar>.
*/
static Obj REREADING; // Copy of GAP global variable REREADING
// We store pointers to C global variables as GAP immediate integers. static Obj * ELM_COPS_PLIST(Obj cops, UInt i)
{
UInt val = UInt_ObjInt(ELM_PLIST(cops, i));
val <<= 2; return (Obj *)val;
}
// Assign 'val' to name 'gvar'. 'hasExprCopiesFopies' can be false if this // variable has a non-default value assigned to ExprGVars, CopiesGVars or // FopiesGVars (this is a performance optimisation). If 'giveNameToFunc' // is TRUE then if 'val' is a function without a name it will be given the // name 'gvar'. staticvoid AssGVarInternal(UInt gvar,
Obj val, BOOL hasExprCopiesFopies, BOOL giveNameToFunc)
{
Obj cops; // list of internal copies
Obj * copy; // one copy
UInt i; // loop variable
Obj onam; // object of <name>
// assign the value to the global variable #ifdef HPCGAP if (!VAL_GVAR_INTERN(gvar)) {
Obj expr = ExprGVar(gvar); if (IS_INTOBJ(expr)) {
AssTLRecord(TLVars, INT_INTOBJ(expr), val); return;
}
}
MEMBAR_WRITE(); #endif
VAL_GVAR_INTERN(gvar) = val;
CHANGED_GVAR_LIST( ValGVars, gvar );
// assign name to a function #ifdef HPCGAP if (IS_BAG_REF(val) && REGION(val) == 0) { // public region? #endif if (giveNameToFunc && val != 0 && TNUM_OBJ(val) == T_FUNCTION &&
NAME_FUNC(val) == 0) {
onam = CopyToStringRep(NameGVar(gvar));
MakeImmutable(onam);
SET_NAME_FUNC(val, onam);
CHANGED_BAG(val);
} #ifdef HPCGAP
} #endif
if (!hasExprCopiesFopies) { // No need to perform any of the remaining checks return;
}
// if the global variable was automatic, convert it to normal
SET_ELM_GVAR_LIST( ExprGVars, gvar, 0 );
// assign the value to all the internal copies
cops = ELM_GVAR_LIST( CopiesGVars, gvar ); if ( cops != 0 ) { for ( i = 1; i <= LEN_PLIST(cops); i++ ) {
copy = ELM_COPS_PLIST(cops, i);
*copy = val;
}
}
// if the value is a function, assign it to all the internal fopies
cops = ELM_GVAR_LIST( FopiesGVars, gvar ); #ifdef HPCGAP if (IS_BAG_REF(val) && REGION(val) == 0) { // public region? #endif if ( cops != 0 && val != 0 && TNUM_OBJ(val) == T_FUNCTION ) { for ( i = 1; i <= LEN_PLIST(cops); i++ ) {
copy = ELM_COPS_PLIST(cops, i);
*copy = val;
}
} #ifdef HPCGAP
} #endif
// if the values is not a function, assign the error function elseif ( cops != 0 && val != 0 /* && TNUM_OBJ(val) != T_FUNCTION */ ) { for ( i = 1; i <= LEN_PLIST(cops); i++ ) {
copy = ELM_COPS_PLIST(cops, i);
*copy = ErrorMustEvalToFuncFunc;
}
}
// if this was an unbind, assign the other error function elseif ( cops != 0 /* && val == 0 */ ) { for ( i = 1; i <= LEN_PLIST(cops); i++ ) {
copy = ELM_COPS_PLIST(cops, i);
*copy = ErrorMustHaveAssObjFunc;
}
}
}
void AssGVar(UInt gvar, Obj val)
{
GVarFlagInfo info = GetGVarFlagInfo(gvar);
if (info.gvarWriteFlag != GVarAssignable) { // make certain that the variable is not read only if ((REREADING != True) && info.gvarWriteFlag == GVarReadOnly) {
ErrorMayQuit("Variable: '%g' is read only", (Int)NameGVar(gvar),
0);
}
// Make certain variable is not constant if (info.gvarWriteFlag == GVarConstant) {
ErrorMayQuit("Variable: '%g' is constant", (Int)NameGVar(gvar),
0);
}
}
// This is a kernel-only variant of AssGVar which will change read-only // variables, which is used for constants like: // Time, MemoryAllocated, last, last2, last3 // Does not automatically give a name to functions based on variable name, // as these names are not given by users. void AssGVarWithoutReadOnlyCheck(UInt gvar, Obj val)
{
GVarFlagInfo info = GetGVarFlagInfo(gvar);
// Make certain variable is not constant if (info.gvarWriteFlag == GVarConstant) {
ErrorMayQuit("Variable: '%g' is constant", (Int)NameGVar(gvar), 0);
}
/**************************************************************************** ** *F ValAutoGVar(<gvar>) . . . . . . . . value of an automatic global variable ** ** 'ValAutoGVar' returns the value of the global variable <gvar>. This will ** be 0 if <gvar> has no assigned value. It will also cause a function ** call, if <gvar> is automatic.
*/
Obj ValAutoGVar (
UInt gvar )
{
Obj val;
Obj expr;
Obj func; // function to call for automatic
Obj arg; // argument to pass for automatic
val = ValGVar(gvar);
// if this is an automatic variable, make the function call if ( val == 0 && (expr = ExprGVar(gvar)) != 0 ) {
#ifdef HPCGAP if (IS_INTOBJ(expr)) { // thread-local variable return GetTLRecordField(TLVars, INT_INTOBJ(expr));
} #endif // make the function call
func = ELM_PLIST( expr, 1 );
arg = ELM_PLIST( expr, 2 );
CALL_1ARGS( func, arg );
// if this is still an automatic variable, this is an error
val = ValGVar(gvar); if (val == 0) {
ErrorMayQuit("Variable: automatic variable '%g' must get a value " "by function call",
(Int)NameGVar(gvar), 0);
}
}
// return the value return val;
}
/**************************************************************************** ** *F ValGVarTL(<gvar>) . . . . . . . . value of a global/thread-local variable ** ** 'ValGVarTL' returns the value of the global or thread-local variable ** <gvar>.
*/ #ifdef HPCGAP
Obj ValGVarTL (
UInt gvar )
{
Obj expr;
Obj val;
val = ValGVar(gvar); // is this a thread-local variable? if ( val == 0 && (expr = ExprGVar(gvar)) != 0 ) {
/**************************************************************************** ** *F GVarName(<name>) . . . . . . . . . . . . . . global variable for a name ** ** 'GVarName' returns the global variable with the name <name>.
*/
UInt GVarName(constChar * name)
{ Char gvarbuf[1024]; // temporary copy for namespace constChar * cns; // pointer to current namespace
// First see whether it could be namespace-local:
cns = STATE(CurrNamespace) ? CONST_CSTR_STRING(STATE(CurrNamespace)) : ""; if (*cns) { // only if a namespace is set
size_t len = strlen(name); if (name[len-1] == NSCHAR) {
gap_strlcpy(gvarbuf, name, 512);
gap_strlcat(gvarbuf, cns, sizeof(gvarbuf));
name = gvarbuf;
}
}
/**************************************************************************** ** *F FuncMakeReadOnlyGVar(<self>,<name>) make a global variable read only ** ** 'FuncMakeReadOnlyGVar' implements the function 'MakeReadOnlyGVar'. ** ** 'MakeReadOnlyGVar( <name> )' ** ** 'MakeReadOnlyGVar' make the global variable with the name <name> (which ** must be a GAP string) read only.
*/ static Obj FuncMakeReadOnlyGVar(Obj self, Obj name)
{
RequireStringRep(SELF_NAME, name);
MakeReadOnlyGVar(GVarName(CONST_CSTR_STRING(name))); return 0;
}
/**************************************************************************** ** *F FuncMakeConstantGVar(<self>,<name>) make a global variable constant ** ** 'FuncMakeConstantGVar' implements the function 'MakeConstantGVar'. ** ** 'MakeConstantGVar( <name> )' ** ** 'MakeConstantGVar' make the global variable with the name <name> (which ** must be a GAP string) constant.
*/ static Obj FuncMakeConstantGVar(Obj self, Obj name)
{
RequireStringRep(SELF_NAME, name);
MakeConstantGVar(GVarName(CONST_CSTR_STRING(name))); return 0;
}
/**************************************************************************** ** *F MakeReadWriteGVar( <gvar> ) . . . . . . make a global variable read write
*/ void MakeReadWriteGVar (
UInt gvar )
{ if (IsConstantGVar(gvar)) {
ErrorMayQuit("Variable: '%g' is constant", (Int)NameGVar(gvar), 0);
}
SetGVarWriteState(gvar, GVarAssignable);
}
/**************************************************************************** ** *F FuncMakeReadWriteGVar(<self>,<name>) make a global variable read write ** ** 'FuncMakeReadWriteGVar' implements the function 'MakeReadWriteGVar'. ** ** 'MakeReadWriteGVar( <name> )' ** ** 'MakeReadWriteGVar' make the global variable with the name <name> (which ** must be a GAP string) read and writable.
*/ static Obj FuncMakeReadWriteGVar(Obj self, Obj name)
{
RequireStringRep(SELF_NAME, name);
MakeReadWriteGVar(GVarName(CONST_CSTR_STRING(name))); return 0;
}
/**************************************************************************** ** *F IsReadOnlyGVar( <gvar> ) . . . . . . return status of a global variable
*/ BOOL IsReadOnlyGVar(UInt gvar)
{ return GetGVarFlagInfo(gvar).gvarWriteFlag == GVarReadOnly;
}
/**************************************************************************** ** *F FuncIsReadOnlyGVar( <name> ) . . .handler for GAP function **
*/
/**************************************************************************** ** *F FuncAUTO() . . . . . . . . . . . . . make automatic global variables ** ** 'FuncAUTO' implements the internal function 'AUTO'. ** ** 'AUTO( <func>, <arg>, <name1>, ... )' ** ** 'AUTO' makes the global variables, whose names are given the strings ** <name1>, <name2>, ..., automatic. That means that when the value of one ** of those global variables is requested, then the function <func> is ** called and the argument <arg> is passed. This function call should, ** cause the execution of an assignment to that global variable, otherwise ** an error is signalled.
*/ static Obj FuncAUTO(Obj self, Obj args)
{
Obj func; // the function to call
Obj arg; // the argument to pass
Obj list; // function and argument list
Obj name; // one name (as a GAP string)
UInt gvar; // one global variable
UInt i; // loop variable
// get and check the function
func = ELM_LIST( args, 1 );
RequireFunction(SELF_NAME, func);
// get the argument
arg = ELM_LIST( args, 2 );
// make the list of function and argument
list = NewPlistFromArgs(func, arg);
// make the global variables automatic for ( i = 3; i <= LEN_LIST(args); i++ ) {
name = ELM_LIST( args, i );
RequireStringRep(SELF_NAME, name);
gvar = GVarName( CONST_CSTR_STRING(name) );
SET_ELM_GVAR_LIST( ValGVars, gvar, 0 );
SET_ELM_GVAR_LIST( ExprGVars, gvar, list );
SetHasExprCopiesFopies(gvar, 1);
CHANGED_GVAR_LIST( ExprGVars, gvar );
}
return 0;
}
/**************************************************************************** ** *F iscomplete( <name>, <len> ) . . . . . . . . find the completions of name *F completion( <name>, <len> ) . . . . . . . . find the completions of name
*/ BOOL iscomplete_gvar(Char * name, UInt len)
{ constChar * curr;
UInt i, k;
UInt numGVars;
numGVars = LengthSymbolTable(&GVarSymbolTable); for ( i = 1; i <= numGVars; i++ ) {
curr = CONST_CSTR_STRING( NameGVar( i ) ); for ( k = 0; name[k] != 0 && curr[k] == name[k]; k++ ) ; if (k == len && curr[k] == '\0') returnTRUE;
} returnFALSE;
}
numGVars = LengthSymbolTable(&GVarSymbolTable);
next = 0; for ( i = 1; i <= numGVars; i++ ) { // consider only variables which are currently bound for completion if ( VAL_GVAR_INTERN( i ) || ELM_GVAR_LIST( ExprGVars, i )) {
curr = CONST_CSTR_STRING( NameGVar( i ) ); for ( k = 0; name[k] != 0 && curr[k] == name[k]; k++ ) ; if ( k < len || curr[k] <= name[k] ) continue; if ( next != 0 ) { for ( k = 0; curr[k] != '\0' && curr[k] == next[k]; k++ ) ; if ( k < len || next[k] < curr[k] ) continue;
}
next = curr;
}
}
if ( next != 0 ) { for ( k = 0; next[k] != '\0'; k++ )
name[k] = next[k];
name[k] = '\0';
}
copy = NEW_PLIST_IMM( T_PLIST, numGVars ); for ( i = 1; i <= numGVars; i++ ) { /* Copy the string here, because we do not want members of NameGVars
* accessible to users, as these strings must not be changed */
strcopy = CopyToStringRep( NameGVar( i ) );
SET_ELM_PLIST( copy, i, strcopy );
CHANGED_BAG( copy );
}
SET_LEN_PLIST( copy, numGVars ); return copy;
}
copy = NEW_PLIST_IMM( T_PLIST, numGVars ); for ( i = 1, j = 1; i <= numGVars; i++ ) { if ( VAL_GVAR_INTERN( i ) || ELM_GVAR_LIST( ExprGVars, i ) ) { /* Copy the string here, because we do not want members of * NameGVars accessible to users, as these strings must not be
* changed */
strcopy = CopyToStringRep( NameGVar( i ) );
SET_ELM_PLIST( copy, j, strcopy );
CHANGED_BAG( copy );
j++;
}
}
SET_LEN_PLIST( copy, j - 1 ); return copy;
}
/**************************************************************************** ** *F FuncASS_GVAR( <self>, <gvar>, <val> ) . . . . assign to a global variable
*/ static Obj FuncASS_GVAR(Obj self, Obj gvar, Obj val)
{
RequireStringRep(SELF_NAME, gvar);
AssGVar( GVarName( CONST_CSTR_STRING(gvar) ), val ); return 0;
}
/**************************************************************************** ** *F FuncISB_GVAR( <self>, <gvar> ) . . check assignment of a global variable
*/ static Obj FuncISB_GVAR(Obj self, Obj gvar)
{
RequireStringRep(SELF_NAME, gvar);
/**************************************************************************** ** *F FuncIS_AUTO_GVAR( <self>, <gvar> ) . . check if a global variable is auto
*/
/**************************************************************************** ** *F FuncVAL_GVAR( <self>, <gvar> ) . . contents of a global variable
*/
/**************************************************************************** ** *V CopyAndFopyGVars . . . . . . kernel table of kernel copies and "fopies" ** ** This needs to be kept inside the kernel so that the copies can be updated ** after loading a workspace.
*/ typedefstruct {
Obj * copy;
UInt isFopy; constChar * name;
} StructCopyGVar;
/**************************************************************************** ** *F InitCopyGVar( <name>, <copy> ) . . declare C variable as copy of global ** ** 'InitCopyGVar' makes the C variable <cvar> at address <copy> a copy of ** the global variable named <name> (which must be a kernel string). ** ** The function only registers the information in <CopyAndFopyGVars>. At a ** latter stage one has to call 'UpdateCopyFopyInfo' to actually enter the ** information stored in <CopyAndFopyGVars> into a plain list. ** ** This is OK for garbage collection, but a real problem for saving in any ** event, this information does not really want to be saved because it is ** kernel centred rather than workspace centred.
*/ void InitCopyGVar ( constChar * name ,
Obj * copy )
{ // make a record in the kernel for saving and loading #ifdef HPCGAP
LockSymbolTableForWriting(&GVarSymbolTable); #endif if ( NCopyAndFopyGVars >= MAX_COPY_AND_FOPY_GVARS ) { #ifdef HPCGAP
UnlockSymbolTable(&GVarSymbolTable); #endif
Panic("no room to record CopyGVar");
}
CopyAndFopyGVars[NCopyAndFopyGVars].copy = copy;
CopyAndFopyGVars[NCopyAndFopyGVars].isFopy = 0;
CopyAndFopyGVars[NCopyAndFopyGVars].name = name;
NCopyAndFopyGVars++; #ifdef HPCGAP
UnlockSymbolTable(&GVarSymbolTable); #endif
}
/**************************************************************************** ** *F InitFopyGVar( <name>, <copy> ) . . declare C variable as copy of global ** ** 'InitFopyGVar' makes the C variable <cvar> at address <copy> a (function) ** copy of the global variable <gvar>, whose name is <name>. That means ** that whenever the value of <gvar> is a function, then <cvar> will ** reference the same value (i.e., will hold the same bag identifier). When ** the value of <gvar> is not a function, then <cvar> will reference a ** function that signals the error ``<func> must be a function''. When ** <gvar> has no assigned value, then <cvar> will reference a function that ** signals the error ``<gvar> must have an assigned value''.
*/ void InitFopyGVar ( constChar * name,
Obj * copy )
{ // make a record in the kernel for saving and loading #ifdef HPCGAP
LockSymbolTableForWriting(&GVarSymbolTable); #endif if ( NCopyAndFopyGVars >= MAX_COPY_AND_FOPY_GVARS ) { #ifdef HPCGAP
UnlockSymbolTable(&GVarSymbolTable); #endif
Panic("no room to record FopyGVar");
}
CopyAndFopyGVars[NCopyAndFopyGVars].copy = copy;
CopyAndFopyGVars[NCopyAndFopyGVars].isFopy = 1;
CopyAndFopyGVars[NCopyAndFopyGVars].name = name;
NCopyAndFopyGVars++; #ifdef HPCGAP
UnlockSymbolTable(&GVarSymbolTable); #endif
}
// append the copy to the copies list // As C global variables are 4-byte aligned, // we shift them down to make it more likely they // will fit in an immediate integer.
GAP_ASSERT(((UInt)copy & 3) == 0);
PushPlist(cops, ObjInt_UInt((UInt)copy >> 2));
// now copy the value of <gvar> to <cvar>
Obj val = ValGVar(gvar); if ( CopyAndFopyGVars[NCopyAndFopyDone].isFopy ) { if ( val != 0 && IS_FUNC(val) ) {
*copy = val;
} elseif ( val != 0 ) {
*copy = ErrorMustEvalToFuncFunc;
} else {
*copy = ErrorMustHaveAssObjFunc;
}
} else {
*copy = val;
}
} #ifdef HPCGAP
DeclareAllGVars();
UnlockSymbolTable(&GVarSymbolTable); #endif
}
/**************************************************************************** ** *F RemoveCopyFopyInfo() . . . remove the info about copies of gvars from ws
*/ staticvoid RemoveCopyFopyInfo( void )
{ #ifdef HPCGAP
LockSymbolTableForWriting(&GVarSymbolTable); #endif
#ifdef USE_GVAR_BUCKETS
UInt i, k, l;
for (k = 0; k < ARRAY_SIZE(CopiesGVars); ++k) { if (CopiesGVars[k] == 0) continue;
l = LEN_PLIST(CopiesGVars[k]); for ( i = 1; i <= l; i++ )
SET_ELM_PLIST( CopiesGVars[k], i, 0 );
}
for (k = 0; k < ARRAY_SIZE(FopiesGVars); ++k) { if (FopiesGVars[k] == 0) continue;
l = LEN_PLIST(FopiesGVars[k]); for ( i = 1; i <= l; i++ )
SET_ELM_PLIST( FopiesGVars[k], i, 0 );
}
#else
UInt i, l;
l = LEN_PLIST(CopiesGVars); for ( i = 1; i <= l; i++ )
SET_ELM_GVAR_LIST( CopiesGVars, i, 0 );
l = LEN_PLIST(FopiesGVars); for ( i = 1; i <= l; i++ )
SET_ELM_GVAR_LIST( FopiesGVars, i, 0 ); #endif
/**************************************************************************** ** *F DeclareGVar(<gvar>, <name>) . . . . . . declare global variable by name *F GVarValue(<gvar>) . . . . . . . . . return value of <gvar>, 0 if unbound *F GVarObj(<gvar>) . . . . . . . . return value of <gvar>, error if unbound *F GVarFunction(<gvar>) . . return value of <gvar>, error if not a function *F GVarOptFunction(<gvar>) return value of <gvar>, 0 if unbound/no function *F SetGVar(<gvar>, <obj>) . . . . . . . . . . . . . assign <obj> to <gvar>
*/
Obj GVarObj(GVarDescriptor *gvar)
{
Obj result = *(gvar->ref); if (!result)
ErrorQuit("Global variable '%s' not initialized", (UInt)(gvar->name), 0);
MEMBAR_READ(); return result;
}
Obj GVarFunction(GVarDescriptor *gvar)
{
Obj result = *(gvar->ref); if (!result)
ErrorQuit("Global variable '%s' not initialized", (UInt)(gvar->name), 0); if (REGION(result))
ErrorQuit("Global variable '%s' is not a function", (UInt)(gvar->name), 0);
ImpliedWriteGuard(result); if (TNUM_OBJ(result) != T_FUNCTION)
ErrorQuit("Global variable '%s' is not a function", (UInt)(gvar->name), 0);
MEMBAR_READ(); return result;
}
Obj GVarOptFunction(GVarDescriptor *gvar)
{
Obj result = *(gvar->ref); if (!result) return (Obj) 0; if (REGION(result)) return (Obj) 0;
ImpliedWriteGuard(result); if (TNUM_OBJ(result) != T_FUNCTION) return (Obj) 0;
MEMBAR_READ(); return result;
}