Skip to content

Commit

Permalink
Add UpdateStat to allow writing to special variables like 'last'.
Browse files Browse the repository at this point in the history
A very subtle mistake in GAP is for users to use the variables
'last' or 'time', as GAP automatically rewrites these variables
after every statement. Add a special function 'UpdateStat' for
changing these variables, so we can disable 'last := x;'
  • Loading branch information
ChrisJefferson committed Jan 23, 2019
1 parent 2d1c2a1 commit cf468b1
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 34 deletions.
18 changes: 18 additions & 0 deletions doc/ref/mloop.xml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,24 @@ Var="time"/>. This is an integer that holds the number of
milliseconds. Similarly the amount of memory allocated during that
statement (in bytes) is stored in the variable <Ref Var="memory_allocated"/>.

The variables <C>last</C>, <C>last2</C>, <C>last3</C>, <Ref
Var="time"/> and <Ref Var="memory_allocated"/> are all write-protected
by default. They can be changed using the function <Ref Func="UpdateStat"/>.

<ManSection>
<Heading>UpdateStat</Heading>
<Func Name="UpdateStat" Arg='name, value'/>

<Description>
<Ref Func="UpdateStat"/> assigns the variable with name <A>name</A> the
value <A>value</A>. Valid values for <A>name</A> in this version of GAP are
<C>"last"</C>, <C>"last2"</C>, <C>"last3"</C>, <C>"time"</C> and
<C>"memory_allocated"</C>. This does not disable these values being
automatically updated at the end of each executed statement.
</Description>
</ManSection>


</Section>


Expand Down
24 changes: 12 additions & 12 deletions lib/demo.g
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@
#F Demonstration( <file> ) . . . . . . . . . . run a demonstration from file
##
if not IsBound(last) then
last := fail;
UpdateStat("last", fail);
fi;
if not IsBound(last2) then
last2 := fail;
UpdateStat("last2", fail);
fi;
if not IsBound(last3) then
last3 := fail;
UpdateStat("last3", fail);
fi;
if not IsBound(time) then
time := fail;
UpdateStat("time", fail);
fi;


Expand All @@ -47,11 +47,11 @@ BindGlobal( "Demonstration", function( file )
while CHAR_INT( ReadByte( keyboard ) ) <> 'q' do
storedtime := Runtime();
result:=READ_COMMAND_REAL( input, true ); # Executing the command.
time := Runtime()-storedtime;
UpdateStat("time", Runtime()-storedtime);
if Length(result) = 2 then
last3 := last2;
last2 := last;
last := result[2];
UpdateStat("last3", last2);
UpdateStat("last2", last);
UpdateStat("last", result[2]);
View( result[2] );
Print("\n" );
fi;
Expand Down Expand Up @@ -92,12 +92,12 @@ local input,command,exec,result,blank,semic,hash,process,l,view,estream;
estream:=InputTextString( exec );
storedtime := Runtime();
result:=READ_COMMAND_REAL( estream, true ); # Executing the command.
time := Runtime()-storedtime;
UpdateStat("time", Runtime()-storedtime);
CloseStream(estream);
if Length(result) = 2 then
last3 := last2;
last2 := last;
last := result[2];
UpdateStat("last3", last2);
UpdateStat("last2", last);
UpdateStat("last", result[2]);
if view then
View(result[2]);
Print("\n");
Expand Down
59 changes: 47 additions & 12 deletions src/gap.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,7 @@ Obj Shell ( Obj context,
if ( status == STATUS_END && evalResult != 0 ) {

/* remember the value in 'last' */
if (lastDepth >= 3)
AssGVar( Last3, ValGVarTL( Last2 ) );
if (lastDepth >= 2)
AssGVar( Last2, ValGVarTL( Last ) );
if (lastDepth >= 1)
AssGVar( Last, evalResult );
UpdateLast(evalResult, lastDepth);

/* print the result */
if ( ! dualSemicolon ) {
Expand Down Expand Up @@ -283,8 +278,9 @@ Obj Shell ( Obj context,

/* stop the stopwatch */
if (setTime) {
AssGVar( Time, INTOBJ_INT( SyTime() - time ) );
AssGVar(MemoryAllocated, ObjInt_Int8(SizeAllBags - mem));
AssGVar_NoReadOnlyCheck(Time, INTOBJ_INT(SyTime() - time));
AssGVar_NoReadOnlyCheck(MemoryAllocated,
ObjInt_Int8(SizeAllBags - mem));
}

if (STATE(UserHasQuit))
Expand Down Expand Up @@ -1484,6 +1480,42 @@ Obj FuncTHREAD_UI(Obj self)

#endif

void UpdateLast(Obj newLast, Int lastDepth)
{
if (lastDepth >= 3)
AssGVar_NoReadOnlyCheck(Last3, ValGVarTL(Last2));
if (lastDepth >= 2)
AssGVar_NoReadOnlyCheck(Last2, ValGVarTL(Last));
if (lastDepth >= 1)
AssGVar_NoReadOnlyCheck(Last, newLast);
}

void FuncUpdateStat(Obj self, Obj name, Obj newStat)
{
if (!IsStringConv(name) || !IS_STRING_REP(name)) {
ErrorMayQuit("UpdateStat: First argument must be a string", 0, 0);
}
const char * cname = CONST_CSTR_STRING(name);
if (strcmp(cname, "time") == 0) {
AssGVar_NoReadOnlyCheck(Time, newStat);
}
else if (strcmp(cname, "last") == 0) {
AssGVar_NoReadOnlyCheck(Last, newStat);
}
else if (strcmp(cname, "last2") == 0) {
AssGVar_NoReadOnlyCheck(Last2, newStat);
}
else if (strcmp(cname, "last3") == 0) {
AssGVar_NoReadOnlyCheck(Last3, newStat);
}
else if (strcmp(cname, "memory_allocated") == 0) {
AssGVar_NoReadOnlyCheck(MemoryAllocated, newStat);
}
else {
ErrorMayQuit("UpdateStat: Unknown Stat '%g'", (Int)name, 0);
}
}


/****************************************************************************
**
Expand Down Expand Up @@ -1519,15 +1551,18 @@ static StructGVarFunc GVarFuncs[] = {
GVAR_FUNC(QUIT_GAP, -1, "args"),
GVAR_FUNC(FORCE_QUIT_GAP, -1, "args"),
GVAR_FUNC(SHOULD_QUIT_ON_BREAK, 0, ""),
GVAR_FUNC(SHELL, -1, "context, canReturnVoid, canReturnObj, lastDepth, "
"setTime, prompt, promptHook, infile, outfile"),
GVAR_FUNC(SHELL,
-1,
"context, canReturnVoid, canReturnObj, lastDepth, "
"setTime, prompt, promptHook, infile, outfile"),
GVAR_FUNC(KERNEL_INFO, 0, ""),
#ifdef HPCGAP
GVAR_FUNC(THREAD_UI, 0, ""),
#endif
GVAR_FUNC(MASTER_POINTER_NUMBER, 1, "ob"),
GVAR_FUNC(FUNC_BODY_SIZE, 1, "f"),
GVAR_FUNC(BREAKPOINT, 1, "integer"),
GVAR_FUNC(UpdateStat, 2, "string, object"),
{ 0, 0, 0, 0, 0 }

};
Expand Down Expand Up @@ -1589,8 +1624,8 @@ static Int PostRestore (
Last3 = GVarName( "last3" );
Time = GVarName( "time" );
MemoryAllocated = GVarName( "memory_allocated" );
AssGVar(Time, INTOBJ_INT(0));
AssGVar(MemoryAllocated, INTOBJ_INT(0));
AssGVar_NoReadOnlyCheck(Time, INTOBJ_INT(0));
AssGVar_NoReadOnlyCheck(MemoryAllocated, INTOBJ_INT(0));
QUITTINGGVar = GVarName( "QUITTING" );

/* return success */
Expand Down
10 changes: 10 additions & 0 deletions src/gap.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ extern UInt Last2;
*/
extern UInt Last3;

/****************************************************************************
**
*V UpdateLast
**
** Updates Last, Last2 and Last3 by the new result 'newLast'.
** lastDepth chooses any how of these variables should be updated, starting
** always with 'Last'.
*/
void UpdateLast(Obj newLast, Int lastDepth);


/****************************************************************************
**
Expand Down
20 changes: 14 additions & 6 deletions src/gvars.c
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,20 @@ static Obj * ELM_COPS_PLIST(Obj cops, UInt i)
void AssGVar (
UInt gvar,
Obj val )
{
/* make certain that the variable is not read only */
if ((REREADING != True) &&
(ELM_GVAR_LIST(WriteGVars, gvar) == INTOBJ_INT(0))) {
ErrorMayQuit("Variable: '%g' is read only", (Int)NameGVar(gvar), 0);
}

AssGVar_NoReadOnlyCheck(gvar, val);
}

// 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
void AssGVar_NoReadOnlyCheck(UInt gvar, Obj val)
{
Obj cops; /* list of internal copies */
Obj * copy; /* one copy */
Expand All @@ -322,12 +336,6 @@ void AssGVar (
ErrorMayQuit("Variable: '%g' is constant", (Int)NameGVar(gvar), 0L);
}

/* make certain that the variable is not read only */
if ((REREADING != True) &&
(ELM_GVAR_LIST(WriteGVars, gvar) == INTOBJ_INT(0))) {
ErrorMayQuit("Variable: '%g' is read only", (Int)NameGVar(gvar), 0);
}

/* assign the value to the global variable */
#ifdef HPCGAP
if (!VAL_GVAR_INTERN(gvar)) {
Expand Down
4 changes: 4 additions & 0 deletions src/gvars.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,15 @@ extern Obj ErrorMustHaveAssObjFunc;
*F AssGVar(<gvar>,<val>) . . . . . . . . . . . . assign to a global variable
**
** 'AssGVar' assigns the value <val> to the global variable <gvar>.
** 'AssGVar_NoReadOnlyCheck' does the same thing, while not checking if the
** variable is read only.
*/
extern void AssGVar (
UInt gvar,
Obj val );

extern void AssGVar_NoReadOnlyCheck(UInt gvar, Obj val);


/****************************************************************************
**
Expand Down
6 changes: 2 additions & 4 deletions src/streams.c
Original file line number Diff line number Diff line change
Expand Up @@ -396,15 +396,13 @@ static void READ_TEST_OR_LOOP(Obj context)
type = ReadEvalCommand(context, &evalResult, &dualSemicolon);

/* stop the stopwatch */
AssGVar( Time, INTOBJ_INT( SyTime() - oldtime ) );
AssGVar_NoReadOnlyCheck(Time, INTOBJ_INT(SyTime() - oldtime));

/* handle ordinary command */
if ( type == 0 && evalResult != 0 ) {

/* remember the value in 'last' and the time in 'time' */
AssGVar( Last3, ValGVarTL( Last2 ) );
AssGVar( Last2, ValGVarTL( Last ) );
AssGVar( Last, evalResult );
UpdateLast(evalResult, 3);

/* print the result */
if ( ! dualSemicolon ) {
Expand Down

0 comments on commit cf468b1

Please sign in to comment.