diff --git a/lib/function.g b/lib/function.g index 1e744bd5a8..716ed17d09 100644 --- a/lib/function.g +++ b/lib/function.g @@ -347,13 +347,6 @@ BIND_GLOBAL( "LocationFunc", function(func) APPEND_LIST(ret, STRING_INT(line)); return ret; fi; - line := LOCATION_FUNC(func); - if line <> fail then - APPEND_LIST(ret, nam); - APPEND_LIST(ret, ":"); - APPEND_LIST(ret, line); - return ret; - fi; return fail; end); diff --git a/lib/methwhy.g b/lib/methwhy.g index 633c5da75a..73cf809113 100644 --- a/lib/methwhy.g +++ b/lib/methwhy.g @@ -514,16 +514,6 @@ BIND_GLOBAL("PageSource", function ( fun, nr... ) l := STARTLINE_FUNC( fun ); if l <> fail then l := Maximum(l-5, 1); - elif IsKernelFunction(fun) then - # page correct C source file and try to find line in C - # source starting `Obj Func` - s := String(fun); - ss:=SplitString(s,""," <>"); - s := First(ss, a-> ':' in a); - if s <> fail then - ss := SplitString(s,":",""); - l := Concatenation("Obj Func", ss[2]); - fi; fi; fi; fi; diff --git a/src/calls.c b/src/calls.c index 5eb7db9a88..9a10334b16 100644 --- a/src/calls.c +++ b/src/calls.c @@ -53,6 +53,8 @@ #include "saveload.h" #include "stats.h" #include "stringobj.h" +#include "sysfiles.h" +#include "sysroots.h" #include "sysstr.h" #include "vars.h" @@ -60,6 +62,9 @@ #include "hpc/thread.h" #endif +#include +#include + void SET_NAME_FUNC(Obj func, Obj name) { GAP_ASSERT(name == 0 || IS_STRING_REP(name)); @@ -733,8 +738,7 @@ void SortHandlers( UInt byWhat ) HandlerSortingStatus = byWhat; } -const Char * CookieOfHandler ( - ObjFunc hdlr ) +static const Char * CookieOfHandler(ObjFunc hdlr) { UInt i, top, bottom, middle; @@ -761,8 +765,7 @@ const Char * CookieOfHandler ( } } -ObjFunc HandlerOfCookie( - const Char * cookie ) +static ObjFunc HandlerOfCookie(const Char * cookie) { Int i,top,bottom,middle; Int res; @@ -793,6 +796,251 @@ ObjFunc HandlerOfCookie( } } +static ObjFunc SourceHandlerOfFunction(Obj func) +{ + // Profiling replaces the active handlers with DoProf* wrappers, but the + // original handlers remain reachable through PROF_FUNC(func). + if (TNUM_OBJ(PROF_FUNC(func)) == T_FUNCTION) + func = PROF_FUNC(func); + + Int narg = NARG_FUNC(func); + return HDLR_FUNC(func, (0 <= narg && narg <= 6) ? narg : 7); +} + +static const Char * CookieOfFunc(Obj func) +{ + return CookieOfHandler(SourceHandlerOfFunction(func)); +} + + +static const Char * CookieSuffix(const Char * cookie) +{ + if (!cookie) + return NULL; + const Char * pos = strchr(cookie, ':'); + return pos ? pos + 1 : NULL; +} + +static BOOL IsStandardKernelCookieSuffix(const char * suffix) +{ + if (!suffix || !*suffix) + return FALSE; + + while (*suffix) { + if (!isalnum((unsigned char)*suffix) && *suffix != '_') + return FALSE; + suffix++; + } + return TRUE; +} + +static BOOL ResolveKernelCookiePath(const Char * cookie, + Char * path, + size_t size) +{ + // Handler cookies store the original source path; keep relative paths + // working by resolving them against the GAP root search path. + const Char * pos = strchr(cookie, ':'); + if (!pos) + return FALSE; + + size_t len = pos - cookie; + if (len == 0 || len >= size) + return FALSE; + + memcpy(path, cookie, len); + path[len] = 0; + + if (SyIsReadableFile(path) == 0) + return TRUE; + + if (len > 8 && strncmp(path, "GAPROOT/", 8) == 0) { + Char resolved[GAP_PATH_MAX]; + if (SyFindGapRootFile(path + 8, resolved, sizeof(resolved))) { + if (gap_strlcpy(path, resolved, size) < size) + return TRUE; + } + return FALSE; + } + + Char resolved[GAP_PATH_MAX]; + if (SyFindGapRootFile(path, resolved, sizeof(resolved))) { + if (gap_strlcpy(path, resolved, size) < size) + return TRUE; + } + + return FALSE; +} + +static const char * SkipCTrivia(const char * p, UInt * line) +{ + while (*p) { + if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\f' || + *p == '\v') { + p++; + continue; + } + if (*p == '\n') { + (*line)++; + p++; + continue; + } + if (p[0] == '/' && p[1] == '/') { + p += 2; + while (*p && *p != '\n') + p++; + continue; + } + if (p[0] == '/' && p[1] == '*') { + p += 2; + while (*p) { + if (*p == '\n') + (*line)++; + if (p[0] == '*' && p[1] == '/') { + p += 2; + break; + } + p++; + } + continue; + } + break; + } + return p; +} + +static const char * SkipCStringLiteral(const char * p, UInt * line, char quote) +{ + GAP_ASSERT(*p == quote); + p++; + while (*p) { + if (*p == '\n') + (*line)++; + if (*p == '\\' && p[1]) { + p += 2; + continue; + } + if (*p == quote) + return p + 1; + p++; + } + return p; +} + +static BOOL ParseBalancedParens(const char ** pp, UInt * line) +{ + const char * p = *pp; + int depth = 0; + + GAP_ASSERT(*p == '('); + + while (*p) { + p = SkipCTrivia(p, line); + if (!*p) + break; + if (*p == '"' || *p == '\'') { + p = SkipCStringLiteral(p, line, *p); + continue; + } + if (*p == '(') + depth++; + else if (*p == ')') { + depth--; + if (depth == 0) { + *pp = p + 1; + return TRUE; + } + } + p++; + } + + return FALSE; +} + +// Locate a real C function definition by looking for the identifier token, +// parsing the following parameter list, and requiring a '{' before any ';'. +// This skips prototypes, call sites, and matches inside comments or strings. +static BOOL FindKernelDefinitionLine(const char * path, + Obj symbol, + UInt * line_out) +{ + BOOL found = FALSE; + Int fid = SyFopen(path, "r", TRUE); + if (fid == -1) + return FALSE; + + Obj string = SyReadStringFid(fid); + if (SyFclose(fid) == -1 || !string) + return FALSE; + + const size_t symbol_len = GET_LEN_STRING(symbol); + const char * p = CONST_CSTR_STRING(string); + UInt line = 1; + + while (*p) { + p = SkipCTrivia(p, &line); + if (!*p) + break; + + if (*p == '"' || *p == '\'') { + p = SkipCStringLiteral(p, &line, *p); + continue; + } + + if (isalpha((unsigned char)*p) || *p == '_') { + const char * token = p; + UInt token_line = line; + while (isalnum((unsigned char)*p) || *p == '_') + p++; + + if ((size_t)(p - token) == symbol_len && + strncmp(token, CONST_CSTR_STRING(symbol), symbol_len) == 0) { + UInt parse_line = line; + const char * after = SkipCTrivia(p, &parse_line); + if (*after == '(' && ParseBalancedParens(&after, &parse_line)) { + after = SkipCTrivia(after, &parse_line); + if (*after == '{') { + *line_out = token_line; + found = TRUE; + break; + } + } + } + continue; + } + + p++; + } + + return found; +} + +static UInt FindKernelFunctionStartLine(Obj func) +{ + UInt line = 0; + if (!BODY_FUNC(func)) + return 0; + line = GET_STARTLINE_BODY(BODY_FUNC(func)); + if (line) + return line; + + const Char * cookie = CookieOfFunc(func); + const Char * suffix = CookieSuffix(cookie); + if (!IsStandardKernelCookieSuffix(suffix)) + return 0; + + Char path[GAP_PATH_MAX]; + if (!ResolveKernelCookiePath(cookie, path, sizeof(path))) + return 0; + + Obj symbol = MakeImmString(suffix); + if (!FindKernelDefinitionLine(path, symbol, &line)) + return 0; + + SET_STARTLINE_BODY(BODY_FUNC(func), line); + return line; +} + #endif @@ -1081,24 +1329,17 @@ void PrintKernelFunction(Obj func) Obj body = BODY_FUNC(func); Obj filename = body ? GET_FILENAME_BODY(body) : 0; if (filename) { - // A "location" is a string attached exclusively to GAP kernel - // functions; it is derived from the function's "cookie" which has the - // form "FILENAME:FUNCNAME" where `FUNCNAME` is the name of the - // underlying C function. Or at least more or less: in the GAP kernel, - // generally the *real* C function name will be `FuncFUNCNAME`. This - // may be rectified in the future. - if ( GET_LOCATION_BODY(body) ) { - Pr("<> from %g:%g", - (Int)filename, - (Int)GET_LOCATION_BODY(body)); + UInt startline = FindKernelFunctionStartLine(func); + if (startline) { + const Char * cookie = CookieOfFunc(func); + if (IsStandardKernelCookieSuffix(CookieSuffix(cookie))) + Pr("<> from %g:%d", (Int)filename, startline); + else + Pr("<> from %g:%d", (Int)filename, + startline); } - // When compiling GAP code into C code, the gap compiler ("gac") attaches - // the filename and line number of the GAP code from which the C code was - // produced; that's the case we are running into here. - else if ( GET_STARTLINE_BODY(body) ) { - Pr("<> from %g:%d", - (Int)filename, - GET_STARTLINE_BODY(body)); + else { + Pr("<> from %g", (Int)filename, 0); } } else { @@ -1462,13 +1703,8 @@ static Obj FuncFILENAME_FUNC(Obj self, Obj func) static Obj FuncSTARTLINE_FUNC(Obj self, Obj func) { RequireFunction(SELF_NAME, func); - - if (BODY_FUNC(func)) { - UInt sl = GET_STARTLINE_BODY(BODY_FUNC(func)); - if (sl) - return INTOBJ_INT(sl); - } - return Fail; + UInt startline = FindKernelFunctionStartLine(func); + return startline ? INTOBJ_INT(startline) : Fail; } static Obj FuncENDLINE_FUNC(Obj self, Obj func) @@ -1483,18 +1719,6 @@ static Obj FuncENDLINE_FUNC(Obj self, Obj func) return Fail; } -static Obj FuncLOCATION_FUNC(Obj self, Obj func) -{ - RequireFunction(SELF_NAME, func); - - if (BODY_FUNC(func)) { - Obj sl = GET_LOCATION_BODY(BODY_FUNC(func)); - if (sl) - return sl; - } - return Fail; -} - /**************************************************************************** ** *F FuncUNPROFILE_FUNC( , ) . . . . . . . . . . . stop profile @@ -1712,7 +1936,6 @@ static StructGVarFunc GVarFuncs[] = { GVAR_FUNC_1ARGS(UNPROFILE_FUNC, func), GVAR_FUNC_1ARGS(IsKernelFunction, func), GVAR_FUNC_1ARGS(FILENAME_FUNC, func), - GVAR_FUNC_1ARGS(LOCATION_FUNC, func), GVAR_FUNC_1ARGS(STARTLINE_FUNC, func), GVAR_FUNC_1ARGS(ENDLINE_FUNC, func), diff --git a/src/calls.h b/src/calls.h index ec72a61e6b..e9c084bffd 100644 --- a/src/calls.h +++ b/src/calls.h @@ -417,10 +417,6 @@ void InitHandlerFunc(ObjFunc hdlr, const Char * cookie); #ifdef USE_GASMAN -const Char * CookieOfHandler(ObjFunc hdlr); - -ObjFunc HandlerOfCookie(const Char * cookie); - void SortHandlers(UInt byWhat); void CheckAllHandlers(void); diff --git a/src/code.c b/src/code.c index 40268f9de8..490d8d7437 100644 --- a/src/code.c +++ b/src/code.c @@ -197,32 +197,17 @@ void SET_GAPNAMEID_BODY(Obj body, UInt val) BODY_HEADER(body)->filename_or_id = INTOBJ_INT(val); } -// location - -Obj GET_LOCATION_BODY(Obj body) -{ - Obj location = BODY_HEADER(body)->startline_or_location; - return (location && IS_STRING_REP(location)) ? location : 0; -} - -void SET_LOCATION_BODY(Obj body, Obj val) -{ - GAP_ASSERT(IS_STRING_REP(val)); - MakeImmutable(val); - BODY_HEADER(body)->startline_or_location = val; -} - // startline UInt GET_STARTLINE_BODY(Obj body) { - Obj line = BODY_HEADER(body)->startline_or_location; + Obj line = BODY_HEADER(body)->startline; return IS_POS_INTOBJ(line) ? INT_INTOBJ(line) : 0; } void SET_STARTLINE_BODY(Obj body, UInt val) { - BODY_HEADER(body)->startline_or_location = val ? INTOBJ_INT(val) : 0; + BODY_HEADER(body)->startline = val ? INTOBJ_INT(val) : 0; } // endline diff --git a/src/code.h b/src/code.h index 110c673982..fea991799e 100644 --- a/src/code.h +++ b/src/code.h @@ -83,12 +83,8 @@ typedef struct CodeState CodeState; ** 'FILENAME_BODY' is a string containing the file of a function. ** 'STARTLINE_BODY' is the line number where a function starts. ** 'ENDLINE_BODY' is the line number where a function ends. -** 'LOCATION_BODY' is a string describing the location of a function. -** Typically this will be the name of a C function implementing it. ** ** These each have a 'GET' and a 'SET' variant, to read or set the value. -** Note that STARTLINE_BODY and LOCATION_BODY are stored in the same place, -** so writing one will overwrite the other. ** ** All of these variables may be 0, if the information is not known, */ @@ -99,12 +95,9 @@ typedef struct { // of the filename inside FilenameCache Obj filename_or_id; - // if non-zero, this is either an immediate integer encoding the - // line number where a function starts, or string describing the - // location of a function. Typically this will be the name of a C - // function implementing it. See also `SetupFuncInfo` which derives - // it from the "cookie" associated to functions. - Obj startline_or_location; + // if non-zero, this is an immediate integer encoding the line + // number where a function starts + Obj startline; // if non-zero, this is an immediate integer encoding the line // number where a function ends @@ -128,13 +121,6 @@ void SET_FILENAME_BODY(Obj body, Obj val); UInt GET_GAPNAMEID_BODY(Obj body); void SET_GAPNAMEID_BODY(Obj body, UInt val); -// see documentation of `startline_or_location` for more information about the -// "location" of a body -Obj GET_LOCATION_BODY(Obj body); -void SET_LOCATION_BODY(Obj body, Obj val); - -// see documentation of `startline_or_location` for more information about the -// "startline" of a body UInt GET_STARTLINE_BODY(Obj body); void SET_STARTLINE_BODY(Obj body, UInt val); UInt GET_ENDLINE_BODY(Obj body); diff --git a/src/modules.c b/src/modules.c index 6b1b689c6b..ff1c046bb1 100644 --- a/src/modules.c +++ b/src/modules.c @@ -611,31 +611,33 @@ void InitGVarOpersFromTable(const StructGVarOper * tab) static void SetupFuncInfo(Obj func, const Char * cookie) { // The string usually has the form "PATH/TO/FILE.c:FUNCNAME". - // We check if that is the case, and if so, split it into the parts before - // and after the colon. In addition, the file path is cut to only contain - // the last two '/'-separated components. + // We check if that is the case, and if so, extract the file path before + // the colon. In addition, the file path is cut to only contain the last + // two '/'-separated components. const Char * pos = strchr(cookie, ':'); if (pos) { - Obj location = MakeImmString(pos + 1); - - Obj filename; - char buffer[512]; - Int len = 511 < (pos - cookie) ? 511 : pos - cookie; - memcpy(buffer, cookie, len); - buffer[len] = 0; - - Char * start = strrchr(buffer, '/'); - if (start) { - while (start > buffer && *(start - 1) != '/') + Obj filename; + const Char * start = pos; + + while (start > cookie && *(start - 1) != '/') + start--; + if (start > cookie) { + start--; + while (start > cookie && *(start - 1) != '/') start--; } - else - start = buffer; - filename = MakeImmString(start); + + if (cookie[0] == '/') { + filename = MakeImmStringWithLen(start, pos - start); + } + else { + filename = MakeString("GAPROOT/"); + AppendCStr(filename, start, pos - start); + MakeImmutableNoRecurse(filename); + } Obj body_bag = NewFunctionBody(); SET_FILENAME_BODY(body_bag, filename); - SET_LOCATION_BODY(body_bag, location); SET_BODY_FUNC(func, body_bag); CHANGED_BAG(body_bag); CHANGED_BAG(func); diff --git a/src/modules.h b/src/modules.h index 1c21120aed..7ea98dc8d8 100644 --- a/src/modules.h +++ b/src/modules.h @@ -201,7 +201,7 @@ typedef struct { // StructGVarFilt arrays #define GVAR_FILT(name, argument, filter) \ { \ - #name, argument, filter, Filt##name, __FILE__ ":" #name \ + #name, argument, filter, Filt##name, __FILE__ ":Filt" #name \ } @@ -221,7 +221,7 @@ typedef struct { // StructGVarAttr arrays #define GVAR_ATTR(name, argument, filter) \ { \ - #name, argument, filter, Attr##name, __FILE__ ":" #name \ + #name, argument, filter, Attr##name, __FILE__ ":Attr" #name \ } @@ -265,7 +265,7 @@ typedef struct { // StructGVarOper arrays #define GVAR_OPER(name, nargs, args, operation) \ { \ - #name, nargs, args, operation, Func##name, __FILE__ ":" #name \ + #name, nargs, args, operation, Func##name, __FILE__ ":Func" #name \ } // The following helper macros are similar to GVAR_FUNC, but perform stricter @@ -275,43 +275,43 @@ typedef struct { #define GVAR_OPER_0ARGS(name, operation) \ { \ #name, 0, "", operation, \ - VALIDATE_FUNC_0ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_0ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_OPER_1ARGS(name, a1, operation) \ { \ #name, 1, #a1, operation, \ - VALIDATE_FUNC_1ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_1ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_OPER_2ARGS(name, a1, a2, operation) \ { \ #name, 2, #a1 "," #a2, operation, \ - VALIDATE_FUNC_2ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_2ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_OPER_3ARGS(name, a1, a2, a3, operation) \ { \ #name, 3, #a1 "," #a2 "," #a3, operation, \ - VALIDATE_FUNC_3ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_3ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_OPER_4ARGS(name, a1, a2, a3, a4, operation) \ { \ #name, 4, #a1 "," #a2 "," #a3 "," #a4, operation, \ - VALIDATE_FUNC_4ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_4ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_OPER_5ARGS(name, a1, a2, a3, a4, a5, operation) \ { \ #name, 5, #a1 "," #a2 "," #a3 "," #a4 "," #a5, operation, \ - VALIDATE_FUNC_5ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_5ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_OPER_6ARGS(name, a1, a2, a3, a4, a5, a6, operation) \ { \ #name, 6, #a1 "," #a2 "," #a3 "," #a4 "," #a5 "," #a6, operation, \ - VALIDATE_FUNC_6ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_6ARGS(Func##name), __FILE__ ":Func" #name \ } @@ -341,49 +341,49 @@ typedef struct { #define GVAR_FUNC_0ARGS(name) \ { \ #name, 0, "", \ - VALIDATE_FUNC_0ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_0ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_FUNC_1ARGS(name, a1) \ { \ #name, 1, #a1, \ - VALIDATE_FUNC_1ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_1ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_FUNC_2ARGS(name, a1, a2) \ { \ #name, 2, #a1 "," #a2, \ - VALIDATE_FUNC_2ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_2ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_FUNC_3ARGS(name, a1, a2, a3) \ { \ #name, 3, #a1 "," #a2 "," #a3, \ - VALIDATE_FUNC_3ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_3ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_FUNC_4ARGS(name, a1, a2, a3, a4) \ { \ #name, 4, #a1 "," #a2 "," #a3 "," #a4, \ - VALIDATE_FUNC_4ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_4ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_FUNC_5ARGS(name, a1, a2, a3, a4, a5) \ { \ #name, 5, #a1 "," #a2 "," #a3 "," #a4 "," #a5, \ - VALIDATE_FUNC_5ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_5ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_FUNC_6ARGS(name, a1, a2, a3, a4, a5, a6) \ { \ #name, 6, #a1 "," #a2 "," #a3 "," #a4 "," #a5 "," #a6, \ - VALIDATE_FUNC_6ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_6ARGS(Func##name), __FILE__ ":Func" #name \ } #define GVAR_FUNC_XARGS(name, nargs, args) \ { \ #name, nargs, args, \ - VALIDATE_FUNC_1ARGS(Func##name), __FILE__ ":" #name \ + VALIDATE_FUNC_1ARGS(Func##name), __FILE__ ":Func" #name \ } diff --git a/src/opers.cc b/src/opers.cc index e2e431f8c9..4ee488ee83 100644 --- a/src/opers.cc +++ b/src/opers.cc @@ -2900,8 +2900,8 @@ static Obj NewGlobalFunction(Obj name, Obj nams) SET_NAME_FUNC(func, namobj); CHANGED_BAG(func); - // We set the location to a description, to make clear the function - // hasn't been defined yet + // Use a descriptive pseudo-filename to make clear the function + // hasn't been defined yet. Obj filename = MakeString("the global function \""); AppendString(filename, namobj); const char * end = "\" is not yet defined"; @@ -2909,7 +2909,6 @@ static Obj NewGlobalFunction(Obj name, Obj nams) Obj body_bag = NewFunctionBody(); SET_FILENAME_BODY(body_bag, filename); - SET_LOCATION_BODY(body_bag, MakeImmString("")); SET_BODY_FUNC(func, body_bag); CHANGED_BAG(body_bag); CHANGED_BAG(func); diff --git a/tst/testinstall/declarefunction.tst b/tst/testinstall/declarefunction.tst index 6604107ce3..23d1d8343a 100644 --- a/tst/testinstall/declarefunction.tst +++ b/tst/testinstall/declarefunction.tst @@ -9,8 +9,8 @@ gap> testfunctionA; function( args... ) ... end gap> Print(testfunctionA,"\n"); function ( args... ) - <> from the global function "testfunctionA" is not yet define\ -d: + <> from the global function "testfunctionA" is no\ +t yet defined end gap> InstallGlobalFunction(testfunctionA, x -> x); gap> testfunctionA; @@ -23,15 +23,15 @@ gap> name := List([1..100], x -> 'a');; gap> func := NEW_GLOBAL_FUNCTION(name);; gap> Print(func,"\n"); function ( args... ) - <> from the global function "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" is not y\ -et defined: + <> from the global function "aaaaaaaaaaaaaaaaaaaa\ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ +aa" is not yet defined end gap> name := List([1..1000], x -> 'a');; gap> func := NEW_GLOBAL_FUNCTION(name);; gap> Print(func,"\n"); function ( args... ) - <> from the global function "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ + <> from the global function "aaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ @@ -44,6 +44,6 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" is not yet defined: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" is not yet defined end gap> STOP_TEST("declarefunction.tst"); diff --git a/tst/testinstall/kernel/calls.tst b/tst/testinstall/kernel/calls.tst index aa765a1775..51bad7d8d5 100644 --- a/tst/testinstall/kernel/calls.tst +++ b/tst/testinstall/kernel/calls.tst @@ -111,12 +111,6 @@ gap> ENDLINE_FUNC(f); 1 gap> ENDLINE_FUNC(IS_OBJECT); fail -gap> LOCATION_FUNC(fail); -Error, LOCATION_FUNC: must be a function (not the value 'fail') -gap> LOCATION_FUNC(f); -fail -gap> LOCATION_FUNC(IS_OBJECT); -fail # gap> UNPROFILE_FUNC(fail); diff --git a/tst/testinstall/opers/LocationFunc.tst b/tst/testinstall/opers/LocationFunc.tst index 8f8515390e..1435dfc7e9 100644 --- a/tst/testinstall/opers/LocationFunc.tst +++ b/tst/testinstall/opers/LocationFunc.tst @@ -1,4 +1,24 @@ gap> START_TEST("LocationFunc.tst"); +gap> FindDefinitionLine := function(path, symbol) +> local lines, i, j; +> if StartsWith(path, "GAPROOT/") then +> path := Filename(List(GAPInfo.RootPaths, Directory), path{[9 .. Length(path)]}); +> fi; +> lines := SplitString(StringFile(path), "\n", ""); +> for i in [1 .. Length(lines)] do +> if StartsWith(lines[i], Concatenation("static Obj ", symbol)) then +> for j in [i .. Length(lines)] do +> if PositionSublist(lines[j], "{") <> fail then +> return i; +> fi; +> if PositionSublist(lines[j], ";") <> fail then +> break; +> fi; +> od; +> fi; +> od; +> return fail; +> end;; # gap> LocationFunc(fail); @@ -18,15 +38,30 @@ true # but we don't want to depend on the changing line numbers, # so we invest some extra work to test the format without # relying on the specific content -gap> loc:=LocationFunc(INSTALL_METHOD_FLAGS);; -gap> StartsWith(loc, "GAPROOT/lib/oper1.g:"); +gap> loc:=SplitString(LocationFunc(INSTALL_METHOD_FLAGS), ":");; +gap> Length(loc); +2 +gap> loc[1] = "GAPROOT/lib/oper1.g"; true -gap> ForAll(loc{[21..Length(loc)]}, IsDigitChar); +gap> ForAll(loc[2], IsDigitChar); true # proper kernel function -gap> LocationFunc(APPEND_LIST_INTR); -"src/listfunc.c:APPEND_LIST_INTR" +gap> loc:=SplitString(LocationFunc(APPEND_LIST_INTR), ":");; +gap> Length(loc); +2 +gap> loc[1] = "GAPROOT/src/listfunc.c"; +true +gap> ForAll(loc[2], IsDigitChar); +true +gap> StartlineFunc(APPEND_LIST_INTR) = Int(loc[2]); +true +gap> StartlineFunc(APPEND_LIST_INTR) = FindDefinitionLine(FilenameFunc(APPEND_LIST_INTR), "FuncAPPEND_LIST_INTR("); +true + +# kernel function with a multiline signature and inline comment +gap> StartlineFunc(ACTIVATE_PROFILING) = FindDefinitionLine(FilenameFunc(ACTIVATE_PROFILING), "FuncACTIVATE_PROFILING("); +true # String is an attribute, so no information is stored gap> LocationFunc( String ); diff --git a/tst/testinstall/varargs.tst b/tst/testinstall/varargs.tst index ff797d955f..b954849266 100644 --- a/tst/testinstall/varargs.tst +++ b/tst/testinstall/varargs.tst @@ -74,11 +74,8 @@ gap> function(a,b....) end; Syntax error: ) expected in stream:1 function(a,b....) end; ^ -gap> Display(RETURN_FIRST); -# src/gap.c:RETURN_FIRST -function ( first, rest... ) - <> from src/gap.c:RETURN_FIRST -end +gap> RETURN_FIRST; +function( first, rest... ) ... end gap> [1..2]; [ 1, 2 ] gap> [1...2]; diff --git a/tst/testspecial/print-compiled-func.g b/tst/testspecial/print-compiled-func.g index 11aa0c1121..5ec69877dc 100644 --- a/tst/testspecial/print-compiled-func.g +++ b/tst/testspecial/print-compiled-func.g @@ -1,2 +1,7 @@ +INSTALL_METHOD_FLAGS; Print(INSTALL_METHOD_FLAGS,"\n"); -Display(InstallMethod); +Display(INSTALL_METHOD_FLAGS); +# +RETURN_FIRST; +Print(RETURN_FIRST,"\n"); +Display(RETURN_FIRST); diff --git a/tst/testspecial/print-compiled-func.g.out b/tst/testspecial/print-compiled-func.g.out index f9010f4919..02bd8f1920 100644 --- a/tst/testspecial/print-compiled-func.g.out +++ b/tst/testspecial/print-compiled-func.g.out @@ -1,10 +1,24 @@ +gap> INSTALL_METHOD_FLAGS; +function( opr, info, rel, flags, baserank, method ) ... end gap> Print(INSTALL_METHOD_FLAGS,"\n"); function ( opr, info, rel, flags, baserank, method ) <> from GAPROOT/lib/oper1.g:LINE end -gap> Display(InstallMethod); +gap> Display(INSTALL_METHOD_FLAGS); # GAPROOT/lib/oper1.g:LINE -function ( arg... ) +function ( opr, info, rel, flags, baserank, method ) <> from GAPROOT/lib/oper1.g:LINE end +gap> # +gap> RETURN_FIRST; +function( first, rest... ) ... end +gap> Print(RETURN_FIRST,"\n"); +function ( first, rest... ) + <> from GAPROOT/src/gap.c:LINE +end +gap> Display(RETURN_FIRST); +# GAPROOT/src/gap.c:LINE +function ( first, rest... ) + <> from GAPROOT/src/gap.c:LINE +end gap> QUIT;