Compare commits

...
12 Commits
Author SHA1 Message Date
pooryorick 0323d7be9f merge pyk-listdictstringrep 2016-07-28 21:49:35 +00:00
pooryorick a237be3c34 merge pyk-pkgrequirenre 2016-07-28 21:47:29 +00:00
pooryorick d978a572d9 merge pyk-emptysting 2016-07-28 21:45:00 +00:00
pooryorick 071312f423 Create new branch named "pyk-trunk" 2016-07-28 21:43:03 +00:00
pooryorick b65a1d9e91 Remove left-over debugging code. 2016-07-28 19:46:14 +00:00
pooryorick 07441b8f6f Converting to stringType no longer loses the existing internal representation
of a value.
2016-07-26 18:36:41 +00:00
pooryorick 583c12ef8c Avoid generating string for list dict list round trip. 2016-07-22 19:27:38 +00:00
pooryorick 726d733b07 NRE-enable [package ifneeded] scripts.
2018-02-06:  This branch is closed. See the py-package-nre branch for an updated version of this work.
2016-07-18 05:49:48 +00:00
pooryorick 382ff46e71 Remove unnecessary macro. Use an enum for result values. 2016-07-11 09:16:15 +00:00
pooryorick 0af0a4ef07 Change TclIsEmpty to TclCheckEmptyString. Check length instead of first char of
bytes.
2016-07-11 08:09:33 +00:00
pooryorick ee2b083ea7 Add missing "break". 2016-07-11 00:01:58 +00:00
pooryorick 2c4866bd92 Avoid generating string representation for comparisons against empty string. 2016-07-10 23:07:47 +00:00
14 changed files with 818 additions and 326 deletions
+1 -1
View File
@@ -234,7 +234,7 @@ static const CmdInfo builtInCmds[] = {
{"lsearch", Tcl_LsearchObjCmd, NULL, NULL, CMD_IS_SAFE},
{"lset", Tcl_LsetObjCmd, TclCompileLsetCmd, NULL, CMD_IS_SAFE},
{"lsort", Tcl_LsortObjCmd, NULL, NULL, CMD_IS_SAFE},
{"package", Tcl_PackageObjCmd, NULL, NULL, CMD_IS_SAFE},
{"package", Tcl_PackageObjCmd, NULL, TclNRPackageObjCmd, CMD_IS_SAFE},
{"proc", Tcl_ProcObjCmd, NULL, NULL, CMD_IS_SAFE},
{"regexp", Tcl_RegexpObjCmd, TclCompileRegexpCmd, NULL, CMD_IS_SAFE},
{"regsub", Tcl_RegsubObjCmd, TclCompileRegsubCmd, NULL, CMD_IS_SAFE},
+60 -25
View File
@@ -62,6 +62,8 @@ static int DictWithCmd(ClientData dummy, Tcl_Interp *interp,
static void DupDictInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr);
static void FreeDictInternalRep(Tcl_Obj *dictPtr);
static void InvalidateDictChain(Tcl_Obj *dictObj);
static void InvalidateListRep(Tcl_Obj *dictObj);
static void InvalidateOtherReps(Tcl_Obj *dictObj);
static int SetDictFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
static void UpdateStringOfDict(Tcl_Obj *dictPtr);
static Tcl_HashEntry * AllocChainEntry(Tcl_HashTable *tablePtr,void *keyPtr);
@@ -426,6 +428,7 @@ FreeDictInternalRep(
Tcl_Obj *dictPtr)
{
Dict *dict = DICT(dictPtr);
InvalidateListRep(dictPtr);
dict->refcount--;
if (dict->refcount <= 0) {
@@ -497,13 +500,24 @@ UpdateStringOfDict(
const char *elem;
char *dst;
const int maxFlags = UINT_MAX / sizeof(int);
Tcl_Obj *listObj;
int numElems;
if (dictPtr->internalRep.twoPtrValue.ptr2 != NULL) {
listObj = dictPtr->internalRep.twoPtrValue.ptr2;
TclGetString(listObj);
dictPtr->bytes = listObj->bytes;
dictPtr->length = listObj->length;
listObj->bytes = NULL;
return;
}
/*
* This field is the most useful one in the whole hash structure, and it
* is not exposed by any API function...
*/
int numElems = dict->table.numEntries * 2;
numElems = dict->table.numEntries * 2;
/* Handle empty list case first, simplifies what follows */
if (numElems == 0) {
@@ -603,8 +617,9 @@ SetDictFromAny(
Tcl_Obj *objPtr)
{
Tcl_HashEntry *hPtr;
int isNew;
int isNew, needlist = 0;
Dict *dict = ckalloc(sizeof(Dict));
Tcl_Obj *listObj;
InitChainTable(dict);
@@ -614,12 +629,14 @@ SetDictFromAny(
* the conversion from lists to dictionaries.
*/
if (objPtr->typePtr == &tclListType) {
listObj = TclObjLookupTyped(objPtr, &tclListType);
if (listObj != NULL) {
int objc, i;
Tcl_Obj **objv;
/* Cannot fail, we already know the Tcl_ObjType is "list". */
TclListObjGetElements(NULL, objPtr, &objc, &objv);
TclListObjGetElements(NULL, listObj, &objc, &objv);
if (objc & 1) {
goto missingValue;
}
@@ -630,16 +647,8 @@ SetDictFromAny(
hPtr = CreateChainEntry(dict, objv[i], &isNew);
if (!isNew) {
Tcl_Obj *discardedValue = Tcl_GetHashValue(hPtr);
/*
* Not really a well-formed dictionary as there are duplicate
* keys, so better get the string rep here so that we can
* convert back.
*/
(void) Tcl_GetString(objPtr);
TclDecrRefCount(discardedValue);
needlist = 1;
Tcl_DecrRefCount(discardedValue);
}
Tcl_SetHashValue(hPtr, objv[i+1]);
Tcl_IncrRefCount(objv[i+1]); /* Since hash now holds ref to it */
@@ -710,12 +719,21 @@ SetDictFromAny(
* Tcl_GetStringFromObj, to use that old internalRep.
*/
TclFreeIntRep(objPtr);
dict->epoch = 0;
dict->chain = NULL;
dict->refcount = 1;
if (needlist) {
/* squirrel the list intrep away for use by SetListFromAny */
listObj = Tcl_DuplicateObj(listObj);
Tcl_IncrRefCount(listObj);
}
TclFreeIntRep(objPtr);
if (needlist) {
objPtr->internalRep.twoPtrValue.ptr2 = listObj;
} else {
objPtr->internalRep.twoPtrValue.ptr2 = NULL;
}
DICT(objPtr) = dict;
objPtr->internalRep.twoPtrValue.ptr2 = NULL;
objPtr->typePtr = &tclDictType;
return TCL_OK;
@@ -866,7 +884,7 @@ InvalidateDictChain(
Dict *dict = DICT(dictObj);
do {
TclInvalidateStringRep(dictObj);
InvalidateOtherReps(dictObj);
dict->epoch++;
dictObj = dict->chain;
if (dictObj == NULL) {
@@ -877,6 +895,25 @@ InvalidateDictChain(
} while (dict != NULL);
}
static void InvalidateListRep(
Tcl_Obj *dictObj
) {
Tcl_Obj *listObj;
if (dictObj->internalRep.twoPtrValue.ptr2 != NULL) {
listObj = dictObj->internalRep.twoPtrValue.ptr2;
Tcl_DecrRefCount(listObj);
dictObj->internalRep.twoPtrValue.ptr2 = NULL;
}
}
static void InvalidateOtherReps(
Tcl_Obj * dictObj
) {
TclInvalidateStringRep(dictObj);
InvalidateListRep(dictObj);
}
/*
*----------------------------------------------------------------------
*
@@ -917,7 +954,7 @@ Tcl_DictObjPut(
}
if (dictPtr->bytes != NULL) {
TclInvalidateStringRep(dictPtr);
InvalidateOtherReps(dictPtr);
}
dict = DICT(dictPtr);
hPtr = CreateChainEntry(dict, keyPtr, &isNew);
@@ -1017,7 +1054,7 @@ Tcl_DictObjRemove(
dict = DICT(dictPtr);
if (DeleteChainEntry(dict, keyPtr)) {
if (dictPtr->bytes != NULL) {
TclInvalidateStringRep(dictPtr);
InvalidateOtherReps(dictPtr);
}
dict->epoch++;
}
@@ -1631,7 +1668,7 @@ DictReplaceCmd(
dictPtr = Tcl_DuplicateObj(dictPtr);
}
if (dictPtr->bytes != NULL) {
TclInvalidateStringRep(dictPtr);
InvalidateOtherReps(dictPtr);
}
for (i=2 ; i<objc ; i+=2) {
Tcl_DictObjPut(NULL, dictPtr, objv[i], objv[i+1]);
@@ -1681,9 +1718,7 @@ DictRemoveCmd(
if (Tcl_IsShared(dictPtr)) {
dictPtr = Tcl_DuplicateObj(dictPtr);
}
if (dictPtr->bytes != NULL) {
TclInvalidateStringRep(dictPtr);
}
InvalidateOtherReps(dictPtr);
for (i=2 ; i<objc ; i++) {
Tcl_DictObjRemove(NULL, dictPtr, objv[i]);
}
@@ -2155,7 +2190,7 @@ DictIncrCmd(
}
}
if (code == TCL_OK) {
TclInvalidateStringRep(dictPtr);
InvalidateOtherReps(dictPtr);
valuePtr = Tcl_ObjSetVar2(interp, objv[1], NULL,
dictPtr, TCL_LEAVE_ERR_MSG);
if (valuePtr == NULL) {
@@ -2244,7 +2279,7 @@ DictLappendCmd(
if (allocatedValue) {
Tcl_DictObjPut(NULL, dictPtr, objv[2], valuePtr);
} else if (dictPtr->bytes != NULL) {
TclInvalidateStringRep(dictPtr);
InvalidateOtherReps(dictPtr);
}
resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,
+49 -5
View File
@@ -5437,22 +5437,58 @@ TEBCresume(
}
} else {
/*
* strcmp can't do a simple memcmp in order to handle the
* special Tcl \xC0\x80 null encoding for utf-8.
* In order to handle the special Tcl \xC0\x80 null encoding
* for utf-8, strcmp can't do a simple memcmp.
*/
s1 = TclGetStringFromObj(valuePtr, &s1len);
s2 = TclGetStringFromObj(value2Ptr, &s2len);
if (TclCheckEmptyString(valuePtr) > 0) {
s1 = "";
s1len = 0;
switch (TclCheckEmptyString(value2Ptr)) {
case -1:
s2 = TclGetStringFromObj(value2Ptr, &s2len);
break;
case 0:
/* Synthesize a value for comparison */
s2 = "1";
s2len = 1;
break;
case 1:
s2 = "";
s2len = 0;
}
} else if (TclCheckEmptyString(value2Ptr) > 0) {
s2 = "";
s2len = 0;
switch (TclCheckEmptyString(valuePtr)) {
case -1:
s1 = TclGetStringFromObj(valuePtr, &s1len);
break;
case 0:
/* Synthesize a value for comparison */
s1 = "1";
s1len = 1;
break;
case 1:
s1 = "";
s1len = 0;
}
} else {
s1 = TclGetStringFromObj(valuePtr, &s1len);
s2 = TclGetStringFromObj(value2Ptr, &s2len);
}
if (checkEq) {
memCmpFn = memcmp;
} else {
memCmpFn = (memCmpFn_t) TclpUtfNcmp2;
}
}
if (checkEq && (s1len != s2len)) {
match = 1;
} else {
} else {
/*
* The comparison function should compare up to the minimum
* byte length only.
@@ -6163,6 +6199,14 @@ TEBCresume(
value2Ptr = OBJ_AT_TOS;
valuePtr = OBJ_UNDER_TOS;
/*
Try to determine, without triggering generation of a string
representation, whether one value is not a number.
*/
if (TclCheckEmptyString(valuePtr) > 1 || TclCheckEmptyString(value2Ptr) > 1) {
goto stringCompare;
}
if (GetNumberFromObj(NULL, valuePtr, &ptr1, &type1) != TCL_OK) {
/*
* At least one non-numeric argument - compare as strings.
+37
View File
@@ -883,6 +883,7 @@ typedef struct VarInHash {
(TclIsVarDirectModifyable(varPtr) &&\
(!(arrayPtr) || !((arrayPtr)->flags & (VAR_TRACED_READ|VAR_TRACED_WRITE))))
/*
*----------------------------------------------------------------
* Data structures related to procedures. These are used primarily in
@@ -2424,6 +2425,7 @@ typedef struct List {
#define TclListObjIsCanonical(listPtr) \
(((listPtr)->typePtr == &tclListType) ? ListObjIsCanonical((listPtr)) : 0)
/*
* Modes for collecting (or not) in the implementations of TclNRForeachCmd,
* TclNRLmapCmd and their compilations.
@@ -2510,6 +2512,28 @@ typedef struct List {
#define DICT_PATH_NON_EXISTENT ((Tcl_Obj *) (void *) 1)
/*
* Macros for tclStringType objects
*/
#define TclStringGetPrev(objPtr) \
((Tcl_Obj *)((objPtr)->internalRep.twoPtrValue.ptr2))
#define TclStringSetPrev(objPtr,prevPtr) \
do { \
(objPtr)->internalRep.twoPtrValue.ptr2 = (void *) (prevPtr); \
} while (0)
#define TclStringInvalidatePrev(objPtr) \
do { \
if (TclStringGetPrev((objPtr)) != NULL) { \
Tcl_DecrRefCount(TclStringGetPrev((objPtr))); \
} \
(objPtr)->internalRep.twoPtrValue.ptr2 = NULL; \
} while (0)
#define TclStringPrevIs(objPtr, checkTypePtr) \
(TclStringGetPrev((objPtr))->typePtr == &(checkTypePtr))
/*
*----------------------------------------------------------------
* Data structures related to the filesystem internals
@@ -2746,6 +2770,10 @@ MODULE_SCOPE long tclObjsShared[TCL_MAX_SHARED_OBJ_STATS];
MODULE_SCOPE char * tclEmptyStringRep;
MODULE_SCOPE char tclEmptyString;
enum CheckEmptyStringResult {
TCL_EMPTYSTRING_UNKNOWN = -1, TCL_EMPTYSTRING_NO, TCL_EMPTYSTRING_YES
};
/*
*----------------------------------------------------------------
* Procedures shared among Tcl modules but not used by the outside world,
@@ -2761,6 +2789,7 @@ MODULE_SCOPE Tcl_ObjCmdProc TclNRForObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclNRForeachCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclNRIfObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclNRLmapCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclNRPackageObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclNRSourceObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclNRSubstObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclNRSwitchObjCmd;
@@ -3037,6 +3066,7 @@ MODULE_SCOPE void TclObjVarErrMsg(Tcl_Interp *interp, Tcl_Obj *part1Ptr,
MODULE_SCOPE int TclObjInvokeNamespace(Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[],
Tcl_Namespace *nsPtr, int flags);
MODULE_SCOPE Tcl_Obj * TclObjLookupTyped (Tcl_Obj * objPtr, Tcl_ObjType *typeptr);
MODULE_SCOPE int TclObjUnsetVar2(Tcl_Interp *interp,
Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, int flags);
MODULE_SCOPE int TclParseBackslash(const char *src,
@@ -3934,6 +3964,7 @@ MODULE_SCOPE int TclCompileStreqOpCmd(Tcl_Interp *interp,
MODULE_SCOPE int TclCompileAssembleCmd(Tcl_Interp *interp,
Tcl_Parse *parsePtr, Command *cmdPtr,
struct CompileEnv *envPtr);
MODULE_SCOPE int TclCheckEmptyString(Tcl_Obj *objPtr);
/*
* Functions defined in generic/tclVar.c and currenttly exported only for use
@@ -4419,6 +4450,12 @@ MODULE_SCOPE void TclDbInitNewObj(Tcl_Obj *objPtr, const char *file,
#define TclIsPureByteArray(objPtr) \
(((objPtr)->typePtr==&tclByteArrayType) && ((objPtr)->bytes==NULL))
#define TclIsPureDict(objPtr) \
(((objPtr)->bytes==NULL) && ((objPtr)->typePtr==&tclDictType))
#define TclIsPureList(objPtr) \
(((objPtr)->bytes==NULL) && ((objPtr)->typePtr==&tclListType))
/*
*----------------------------------------------------------------
* Macro used by the Tcl core to compare Unicode strings. On big-endian
+18 -7
View File
@@ -13,6 +13,7 @@
#include "tclInt.h"
#define LIST(listObj) ((List *)(listObj)->internalRep.twoPtrValue.ptr1)
/*
* Prototypes for functions defined later in this file:
*/
@@ -20,8 +21,8 @@
static List * AttemptNewList(Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
static List * NewListIntRep(int objc, Tcl_Obj *const objv[], int p);
static void DupListInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr);
static void FreeListInternalRep(Tcl_Obj *listPtr);
static void DupListInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr);
static int SetListFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
static void UpdateStringOfList(Tcl_Obj *listPtr);
@@ -1822,18 +1823,28 @@ SetListFromAny(
Tcl_Interp *interp, /* Used for error reporting if not NULL. */
Tcl_Obj *objPtr) /* The object to convert. */
{
Tcl_Obj *listPtr;
List *listRepPtr;
Tcl_Obj **elemPtrs;
/*
* Dictionaries are a special case; they have a string representation such
* that *all* valid dictionaries are valid lists. Hence we can convert
* more directly. Only do this when there's no existing string rep; if
* there is, it is the string rep that's authoritative (because it could
* describe duplicate keys).
* Dictionaries are a special case; they may have stored a previous list
* representation
*/
if (objPtr->typePtr == &tclDictType && !objPtr->bytes) {
listPtr = TclObjLookupTyped(objPtr, &tclListType);
if (listPtr != NULL) {
Tcl_IncrRefCount(listPtr);
TclFreeIntRep(objPtr);
DupListInternalRep(listPtr, objPtr);
Tcl_DecrRefCount(listPtr);
objPtr->typePtr = &tclListType;
/* To Do: Maybe keep the dict intrep around in case this object is
* used as a dict again */
return TCL_OK;
}
if (objPtr->typePtr == &tclDictType) {
Tcl_Obj *keyPtr, *valuePtr;
Tcl_DictSearch search;
int done, size;
+40
View File
@@ -4491,6 +4491,46 @@ Tcl_RepresentationCmd(
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* TclLookupObjTyped --
*
* Given a Tcl_Obj, find an corresponding object, if one exists, of a
* given type.
*
* Results:
* The found object, or, if no such corresponding object was found, NULL.
*
*
* Side effects:
* None
*
*----------------------------------------------------------------------
*/
Tcl_Obj *
TclObjLookupTyped (Tcl_Obj * objPtr, Tcl_ObjType *typePtr) {
Tcl_Obj *foundPtr;
if (typePtr == objPtr->typePtr) {
return objPtr;
}
if (objPtr->typePtr == &tclStringType) {
foundPtr = TclStringGetPrev(objPtr);
if (foundPtr == NULL) {
return NULL;
}
return TclObjLookupTyped(foundPtr, typePtr);
}
if (typePtr == &tclListType) {
if (objPtr->typePtr == &tclDictType && objPtr->internalRep.twoPtrValue.ptr2 != NULL) {
/* A usable list representation exists. */
return objPtr->internalRep.twoPtrValue.ptr2;
}
}
return NULL;
}
/*
* Local Variables:
* mode: c
+510 -280
View File
@@ -49,6 +49,29 @@ typedef struct Package {
const void *clientData; /* Client data. */
} Package;
/* PkgRequireState is passed between functions that form a unit of execution but that
* are chunked up for the NRE *
*/
typedef struct PkgRequireState {
const char *name;
int reqc;
int pass;
int code;
Package *pkgPtr;
PkgAvail *availPtr;
PkgAvail *bestPtr;
PkgAvail *bestStablePtr;
char *bestVersion; /* Internal rep. of versions */
char *availVersion;
int availStable;
char *pkgVersionI;
void *clientDataPtr;
char *result;
char *versionToProvide;
Tcl_Obj *scriptObj;
} PkgRequireState;
/*
* Prototypes for functions defined in this file:
*/
@@ -69,10 +92,24 @@ static void AddRequirementsToResult(Tcl_Interp *interp, int reqc,
static void AddRequirementsToDString(Tcl_DString *dstring,
int reqc, Tcl_Obj *const reqv[]);
static Package * FindPackage(Tcl_Interp *interp, const char *name);
static const char * PkgRequireCore(Tcl_Interp *interp, const char *name,
int reqc, Tcl_Obj *const reqv[],
void *clientDataPtr);
/*
This sequence is listed in order of execution. Later in the code they
don't appear in order of execution because they are pushed onto the
callback stack in reverse order of execution, and dynamically as processing
of the callback stack proceeds.
*/
int TclNRPkgRequireProc(ClientData data[], Tcl_Interp *interp, int result);
int PkgRequireCore(ClientData data[], Tcl_Interp *interp, int result);
int PkgRequireCoreUnknown(ClientData data[], Tcl_Interp *interp, int result);
int PkgRequireCoreUnknownEvalFinish(ClientData data[], Tcl_Interp *interp, int result);
int PkgRequireCoreUnknownFinish(ClientData data[], Tcl_Interp *interp, int result);
int PkgRequireCoreBest(ClientData data[], Tcl_Interp *interp, int result);
int PkgRequireCoreBestEvalFinish(ClientData data[], Tcl_Interp *interp, int result);
int PkgRequireCoreBestFinish(ClientData data[], Tcl_Interp *interp, int result);
int PkgRequireCoreCheckFound(ClientData data[], Tcl_Interp *interp, int result);
int PkgRequireCoreFinish(ClientData data[], Tcl_Interp *interp, int result);
int PkgRequireProcFinish(ClientData data[], Tcl_Interp *interp, int result);
/*
* Helper macros.
*/
@@ -299,18 +336,22 @@ Tcl_PkgRequireEx(
*/
if (version == NULL) {
result = PkgRequireCore(interp, name, 0, NULL, clientDataPtr);
if (Tcl_PkgRequireProc(interp, name, 0, NULL, clientDataPtr) == TCL_OK) {
result = Tcl_GetStringResult(interp);
}
} else {
if (exact && TCL_OK
!= CheckVersionAndConvert(interp, version, NULL, NULL)) {
return NULL;
}
ov = Tcl_NewStringObj(version, -1);
Tcl_IncrRefCount(ov);
if (exact) {
Tcl_AppendStringsToObj(ov, "-", version, NULL);
}
Tcl_IncrRefCount(ov);
result = PkgRequireCore(interp, name, 1, &ov, clientDataPtr);
if (Tcl_PkgRequireProc(interp, name, 1, &ov, clientDataPtr) == TCL_OK) {
result = Tcl_GetStringResult(interp);
}
TclDecrRefCount(ov);
}
@@ -328,39 +369,65 @@ Tcl_PkgRequireProc(
* available. */
void *clientDataPtr)
{
const char *result =
PkgRequireCore(interp, name, reqc, reqv, clientDataPtr);
NRE_callback *rootPtr = TOP_CB(interp);
TclNRAddCallback(
interp, TclNRPkgRequireProc, name, INT2PTR(reqc), reqv, clientDataPtr);
return TclNRRunCallbacks(interp, TCL_OK, rootPtr);
}
if (result == NULL) {
int
TclNRPkgRequireProc(
ClientData data[],
Tcl_Interp *interp, /* Interpreter in which package is now
* available. */
int result
) {
PkgRequireState *statePtr;
statePtr = ckalloc(sizeof(PkgRequireState));
statePtr->code = TCL_OK;
statePtr->name = data[0];
statePtr->reqc = PTR2INT(data[1]);
statePtr->pass = 1;
statePtr->clientDataPtr = data[3];
statePtr->result = NULL;
if (TCL_OK != CheckAllRequirements(interp, statePtr->reqc, data[2])) {
return TCL_ERROR;
}
Tcl_SetObjResult(interp, Tcl_NewStringObj(result, -1));
Tcl_NRAddCallback(interp, PkgRequireCore, statePtr, data[2], NULL, NULL);
return TCL_OK;
}
static const char *
PkgRequireCore(
Tcl_Interp *interp, /* Interpreter in which package is now
* available. */
const char *name, /* Name of desired package. */
int reqc, /* Requirements constraining the desired
* version. */
Tcl_Obj *const reqv[], /* 0 means to use the latest version
* available. */
void *clientDataPtr)
{
Interp *iPtr = (Interp *) interp;
Package *pkgPtr;
PkgAvail *availPtr, *bestPtr, *bestStablePtr;
char *availVersion, *bestVersion;
/* Internal rep. of versions */
int availStable, code, satisfies, pass;
char *script, *pkgVersionI;
Tcl_DString command;
if (TCL_OK != CheckAllRequirements(interp, reqc, reqv)) {
return NULL;
int
TclPkgRequireProcFinish(
ClientData data[],
Tcl_Interp *interp,
int result
) {
int objc = PTR2INT(data[0]);
Tcl_Obj *const *objv = data[1];
while (objc-- > 0) {
Tcl_DecrRefCount(objv[objc]);
}
if (data[2] != NULL) {
Tcl_DecrRefCount(data[2]);
}
return result;
}
int
PkgRequireCore(
ClientData data[],
Tcl_Interp *interp,
int result0
) {
PkgRequireState *statePtr = data[0];
Tcl_Obj *const *reqv = data[1];
Interp *iPtr = (Interp *) interp;
int satisfies;
/*
* It can take up to three passes to find the package: one pass to run the
@@ -369,256 +436,380 @@ PkgRequireCore(
* the "package ifneeded" script.
*/
for (pass=1 ;; pass++) {
pkgPtr = FindPackage(interp, name);
if (pkgPtr->version != NULL) {
break;
statePtr->pkgPtr = FindPackage(interp, statePtr->name);
if (statePtr->pkgPtr->version != NULL) {
Tcl_NRAddCallback(interp, PkgRequireCoreCheckFound,
statePtr, (ClientData)reqv, NULL, NULL);
return TCL_OK;
}
/*
* Check whether we're already attempting to load some version of this
* package (circular dependency detection).
*/
if (statePtr->pkgPtr->clientData != NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"circular package dependency:"
" attempt to provide %s %s requires %s",
statePtr->name, (char *) statePtr->pkgPtr->clientData,
statePtr->name));
AddRequirementsToResult(interp, statePtr->reqc, reqv);
Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "CIRCULARITY", NULL);
TclNRAddCallback(
interp, PkgRequireCoreFinish, statePtr, NULL, NULL, NULL);
return TCL_ERROR;
}
/*
* The package isn't yet present. Search the list of available
* versions and invoke the script for the best available version. We
* are actually locating the best, and the best stable version. One of
* them is then chosen based on the selection mode.
*/
statePtr->bestPtr = NULL;
statePtr->bestStablePtr = NULL;
statePtr->bestVersion = NULL;
for (statePtr->availPtr = statePtr->pkgPtr->availPtr; statePtr->availPtr != NULL;
statePtr->availPtr = statePtr->availPtr->nextPtr) {
if (CheckVersionAndConvert(interp, statePtr->availPtr->version,
&statePtr->availVersion, &statePtr->availStable) != TCL_OK) {
/*
* The provided version number has invalid syntax. This
* should not happen. This should have been caught by the
* 'package ifneeded' registering the package.
*/
continue;
}
/*
* Check whether we're already attempting to load some version of this
* package (circular dependency detection).
*/
if (statePtr->bestPtr != NULL) {
int res = CompareVersions(statePtr->availVersion, statePtr->bestVersion, NULL);
if (pkgPtr->clientData != NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"circular package dependency:"
" attempt to provide %s %s requires %s",
name, (char *) pkgPtr->clientData, name));
AddRequirementsToResult(interp, reqc, reqv);
Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "CIRCULARITY", NULL);
return NULL;
}
/*
* Note: Use internal reps!
*/
/*
* The package isn't yet present. Search the list of available
* versions and invoke the script for the best available version. We
* are actually locating the best, and the best stable version. One of
* them is then chosen based on the selection mode.
*/
bestPtr = NULL;
bestStablePtr = NULL;
bestVersion = NULL;
for (availPtr = pkgPtr->availPtr; availPtr != NULL;
availPtr = availPtr->nextPtr) {
if (CheckVersionAndConvert(interp, availPtr->version,
&availVersion, &availStable) != TCL_OK) {
if (res <= 0) {
/*
* The provided version number has invalid syntax. This
* should not happen. This should have been caught by the
* 'package ifneeded' registering the package.
* The version of the package sought is not as good as the
* currently selected version. Ignore it.
*/
ckfree(statePtr->availVersion);
statePtr->availVersion = NULL;
continue;
}
if (bestPtr != NULL) {
int res = CompareVersions(availVersion, bestVersion, NULL);
/*
* Note: Use internal reps!
*/
if (res <= 0) {
/*
* The version of the package sought is not as good as the
* currently selected version. Ignore it.
*/
ckfree(availVersion);
availVersion = NULL;
continue;
}
}
/*
* We have found a version which is better than our max.
*/
if (reqc > 0) {
/* Check satisfaction of requirements. */
satisfies = SomeRequirementSatisfied(availVersion, reqc, reqv);
if (!satisfies) {
ckfree(availVersion);
availVersion = NULL;
continue;
}
}
bestPtr = availPtr;
if (bestVersion != NULL) {
ckfree(bestVersion);
}
bestVersion = availVersion;
/*
* If this new best version is stable then it also has to be
* better than the max stable version found so far.
*/
if (availStable) {
bestStablePtr = availPtr;
}
}
if (bestVersion != NULL) {
ckfree(bestVersion);
}
/*
* Now choose a version among the two best. For 'latest' we simply
* take (actually keep) the best. For 'stable' we take the best
* stable, if there is any, or the best if there is nothing stable.
* We have found a version which is better than our max.
*/
if ((iPtr->packagePrefer == PKG_PREFER_STABLE)
&& (bestStablePtr != NULL)) {
bestPtr = bestStablePtr;
if (statePtr->reqc > 0) {
/* Check satisfaction of requirements. */
satisfies = SomeRequirementSatisfied(
statePtr->availVersion, statePtr->reqc, reqv);
if (!satisfies) {
ckfree(statePtr->availVersion);
statePtr->availVersion = NULL;
continue;
}
}
if (bestPtr != NULL) {
/*
* We found an ifneeded script for the package. Be careful while
* executing it: this could cause reentrancy, so (a) protect the
* script itself from deletion and (b) don't assume that bestPtr
* will still exist when the script completes.
*/
statePtr->bestPtr = statePtr->availPtr;
char *versionToProvide = bestPtr->version;
script = bestPtr->script;
pkgPtr->clientData = versionToProvide;
Tcl_Preserve(script);
Tcl_Preserve(versionToProvide);
code = Tcl_EvalEx(interp, script, -1, TCL_EVAL_GLOBAL);
Tcl_Release(script);
pkgPtr = FindPackage(interp, name);
if (code == TCL_OK) {
Tcl_ResetResult(interp);
if (pkgPtr->version == NULL) {
code = TCL_ERROR;
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"attempt to provide package %s %s failed:"
" no version of package %s provided",
name, versionToProvide, name));
Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "UNPROVIDED",
NULL);
} else {
char *pvi, *vi;
if (CheckVersionAndConvert(interp, pkgPtr->version, &pvi,
NULL) != TCL_OK) {
code = TCL_ERROR;
} else if (CheckVersionAndConvert(interp,
versionToProvide, &vi, NULL) != TCL_OK) {
ckfree(pvi);
code = TCL_ERROR;
} else {
int res = CompareVersions(pvi, vi, NULL);
ckfree(pvi);
ckfree(vi);
if (res != 0) {
code = TCL_ERROR;
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"attempt to provide package %s %s failed:"
" package %s %s provided instead",
name, versionToProvide,
name, pkgPtr->version));
Tcl_SetErrorCode(interp, "TCL", "PACKAGE",
"WRONGPROVIDE", NULL);
}
}
}
} else if (code != TCL_ERROR) {
Tcl_Obj *codePtr = Tcl_NewIntObj(code);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"attempt to provide package %s %s failed:"
" bad return code: %s",
name, versionToProvide, TclGetString(codePtr)));
Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "BADRESULT", NULL);
TclDecrRefCount(codePtr);
code = TCL_ERROR;
}
if (code == TCL_ERROR) {
Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
"\n (\"package ifneeded %s %s\" script)",
name, versionToProvide));
}
Tcl_Release(versionToProvide);
if (code != TCL_OK) {
/*
* Take a non-TCL_OK code from the script as an indication the
* package wasn't loaded properly, so the package system
* should not remember an improper load.
*
* This is consistent with our returning NULL. If we're not
* willing to tell our caller we got a particular version, we
* shouldn't store that version for telling future callers
* either.
*/
if (pkgPtr->version != NULL) {
ckfree(pkgPtr->version);
pkgPtr->version = NULL;
}
pkgPtr->clientData = NULL;
return NULL;
}
break;
if (statePtr->bestVersion != NULL) {
ckfree(statePtr->bestVersion);
}
statePtr->bestVersion = statePtr->availVersion;
/*
* The package is not in the database. If there is a "package unknown"
* command, invoke it (but only on the first pass; after that, we
* should not get here in the first place).
* If this new best version is stable then it also has to be
* better than the max stable version found so far.
*/
if (pass > 1) {
break;
}
script = ((Interp *) interp)->packageUnknown;
if (script != NULL) {
Tcl_DStringInit(&command);
Tcl_DStringAppend(&command, script, -1);
Tcl_DStringAppendElement(&command, name);
AddRequirementsToDString(&command, reqc, reqv);
code = Tcl_EvalEx(interp, Tcl_DStringValue(&command),
Tcl_DStringLength(&command), TCL_EVAL_GLOBAL);
Tcl_DStringFree(&command);
if ((code != TCL_OK) && (code != TCL_ERROR)) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"bad return code: %d", code));
Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "BADRESULT", NULL);
code = TCL_ERROR;
}
if (code == TCL_ERROR) {
Tcl_AddErrorInfo(interp,
"\n (\"package unknown\" script)");
return NULL;
}
Tcl_ResetResult(interp);
if (statePtr->availStable) {
statePtr->bestStablePtr = statePtr->availPtr;
}
}
if (statePtr->bestVersion != NULL) {
ckfree(statePtr->bestVersion);
}
/*
* Now choose a version among the two best. For 'latest' we simply
* take (actually keep) the best. For 'stable' we take the best
* stable, if there is any, or the best if there is nothing stable.
*/
if ((iPtr->packagePrefer == PKG_PREFER_STABLE)
&& (statePtr->bestStablePtr != NULL)) {
statePtr->bestPtr = statePtr->bestStablePtr;
}
if (statePtr->bestPtr != NULL) {
TclNRAddCallback(
interp, PkgRequireCoreBestFinish, statePtr, reqv, NULL, NULL);
TclNRAddCallback(
interp, PkgRequireCoreBest, statePtr, NULL, NULL, NULL);
return TCL_OK;
}
/*
* The package is not in the database. If there is a "package unknown"
* command, invoke it (but only on the first pass; after that, we
* should not get here in the first place).
*/
if (statePtr->pass > 1) {
Tcl_NRAddCallback(interp, PkgRequireCoreCheckFound,
statePtr, (ClientData)reqv, NULL, NULL);
return TCL_OK;
}
TclNRAddCallback(
interp, PkgRequireCoreUnknownFinish, statePtr,reqv, NULL, NULL);
TclNRAddCallback(
interp, PkgRequireCoreUnknown, statePtr,reqv, NULL, NULL);
return TCL_OK;
}
int
PkgRequireCoreUnknown(
ClientData data[],
Tcl_Interp *interp,
int result
) {
Interp *iPtr = (Interp *) interp;
PkgRequireState *statePtr = data[0];
Tcl_Obj *const *reqv = data[1];
Tcl_DString command;
char *script;
script = ((Interp *) interp)->packageUnknown;
if (script != NULL) {
Tcl_DStringInit(&command);
Tcl_DStringAppend(&command, script, -1);
Tcl_DStringAppendElement(&command, statePtr->name);
AddRequirementsToDString(&command, statePtr->reqc, reqv);
statePtr->scriptObj = Tcl_NewStringObj(Tcl_DStringValue(&command), -1);
/* No need to increment of the ref count of scriptObj, because
* TclNREvelObjEx does that. right?
*/
Tcl_DStringFree(&command);
TclNRAddCallback(
interp, PkgRequireCoreUnknownEvalFinish, statePtr, data[1], NULL, NULL);
statePtr->code = TclNREvalObjEx(
interp, statePtr->scriptObj, TCL_EVAL_GLOBAL, iPtr->cmdFramePtr, 1);
}
return result;
}
int
PkgRequireCoreUnknownEvalFinish(
ClientData data[],
Tcl_Interp *interp,
int result
) {
PkgRequireState *statePtr = data[0];
/* Pick up the return code from TclNREvalObjEx */
statePtr->code = result;
if ((statePtr->code != TCL_OK) && (statePtr->code != TCL_ERROR)) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"bad return code: %d", statePtr->code));
Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "BADRESULT", NULL);
statePtr->code = TCL_ERROR;
}
if (statePtr->code == TCL_ERROR) {
Tcl_AddErrorInfo(interp,
"\n (\"package unknown\" script)");
return TCL_ERROR;
}
Tcl_ResetResult(interp);
return TCL_OK;
}
int
PkgRequireCoreUnknownFinish(
ClientData data[],
Tcl_Interp *interp,
int result
) {
PkgRequireState *statePtr = data[0];
if (statePtr->code != TCL_OK) {
/* error message has been set */
TclNRAddCallback(
interp, PkgRequireCoreFinish, statePtr, NULL, NULL, NULL);
return TCL_ERROR;
}
statePtr->pass++;
Tcl_NRAddCallback(interp, PkgRequireCore, statePtr, data[1], NULL, NULL);
return TCL_OK;
}
int
PkgRequireCoreBest(
ClientData data[],
Tcl_Interp *interp,
int result
) {
Interp *iPtr = (Interp *) interp;
PkgRequireState *statePtr = data[0];
char *script;
/*
* We found an ifneeded script for the package. Be careful while
* executing it: this could cause reentrancy, so (a) protect the
* script itself from deletion and (b) don't assume that bestPtr
* will still exist when the script completes.
*/
statePtr->versionToProvide = statePtr->bestPtr->version;
script = statePtr->bestPtr->script;
statePtr->pkgPtr->clientData = statePtr->versionToProvide;
Tcl_Preserve(statePtr->versionToProvide);
statePtr->scriptObj = Tcl_NewStringObj(script, -1);
Tcl_IncrRefCount(statePtr->scriptObj);
TclNRAddCallback(
interp, PkgRequireCoreBestEvalFinish, statePtr, data[1], NULL, NULL);
TclNREvalObjEx(
interp, statePtr->scriptObj, TCL_EVAL_GLOBAL , iPtr->cmdFramePtr, 0);
return result;
}
int
PkgRequireCoreBestEvalFinish(
ClientData data[],
Tcl_Interp *interp,
int result
) {
PkgRequireState *statePtr = data[0];
/* Pick up the return code from TclNREvalObjEx */
statePtr->code = result;
Tcl_DecrRefCount(statePtr->scriptObj);
statePtr->pkgPtr = FindPackage(interp, statePtr->name);
if (statePtr->code == TCL_OK) {
Tcl_ResetResult(interp);
if (statePtr->pkgPtr->version == NULL) {
statePtr->code = TCL_ERROR;
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"attempt to provide package %s %s failed:"
" no version of package %s provided",
statePtr->name, statePtr->versionToProvide, statePtr->name));
Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "UNPROVIDED",
NULL);
} else {
char *pvi, *vi;
if (CheckVersionAndConvert(interp, statePtr->pkgPtr->version, &pvi,
NULL) != TCL_OK) {
statePtr->code = TCL_ERROR;
} else if (CheckVersionAndConvert(interp,
statePtr->versionToProvide, &vi, NULL) != TCL_OK) {
ckfree(pvi);
statePtr->code = TCL_ERROR;
} else {
int res = CompareVersions(pvi, vi, NULL);
ckfree(pvi);
ckfree(vi);
if (res != 0) {
statePtr->code = TCL_ERROR;
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"attempt to provide package %s %s failed:"
" package %s %s provided instead",
statePtr->name, statePtr->versionToProvide,
statePtr->name, statePtr->pkgPtr->version));
Tcl_SetErrorCode(interp, "TCL", "PACKAGE",
"WRONGPROVIDE", NULL);
}
}
}
} else if (statePtr->code != TCL_ERROR) {
Tcl_Obj *codePtr = Tcl_NewIntObj(statePtr->code);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"attempt to provide package %s %s failed:"
" bad return code: %s",
statePtr->name, statePtr->versionToProvide, TclGetString(codePtr)));
Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "BADRESULT", NULL);
TclDecrRefCount(codePtr);
statePtr->code = TCL_ERROR;
}
if (statePtr->code == TCL_ERROR) {
Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
"\n (\"package ifneeded %s %s\" script)",
statePtr->name, statePtr->versionToProvide));
}
return result;
}
int
PkgRequireCoreBestFinish(
ClientData data[],
Tcl_Interp *interp,
int result
) {
PkgRequireState *statePtr = data[0];
if (statePtr->code == TCL_ERROR) {
/*
* Take a non-TCL_OK code from the script as an indication the
* package wasn't loaded properly, so the package system
* should not remember an improper load.
*
* This is consistent with our returning NULL. If we're not
* willing to tell our caller we got a particular version, we
* shouldn't store that version for telling future callers
* either.
*/
Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
"\n (\"package ifneeded %s %s\" script)",
statePtr->name, statePtr->versionToProvide));
if (statePtr->pkgPtr->version != NULL) {
ckfree(statePtr->pkgPtr->version);
statePtr->pkgPtr->version = NULL;
}
statePtr->pkgPtr->clientData = NULL;
TclNRAddCallback(
interp, PkgRequireCoreFinish, statePtr, NULL, NULL, NULL);
} else {
Tcl_NRAddCallback(interp, PkgRequireCoreCheckFound, statePtr, data[1], NULL, NULL);
}
Tcl_Release(statePtr->versionToProvide);
return result;
}
int
PkgRequireCoreCheckFound(
ClientData data[],
Tcl_Interp *interp,
int result
) {
PkgRequireState *statePtr = data[0];
Tcl_Obj *const *reqv = data[1];
Package *pkgPtr = statePtr->pkgPtr;
int satisfies;
Tcl_NRAddCallback(interp, PkgRequireCoreFinish, statePtr, NULL, NULL,NULL);
if (pkgPtr->version == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"can't find package %s", name));
"can't find package %s", statePtr->name));
Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "UNFOUND", NULL);
AddRequirementsToResult(interp, reqc, reqv);
return NULL;
AddRequirementsToResult(interp, statePtr->reqc, reqv);
return TCL_ERROR;
}
/*
@@ -626,30 +817,48 @@ PkgRequireCore(
* provided version meets the current requirements.
*/
if (reqc != 0) {
CheckVersionAndConvert(interp, pkgPtr->version, &pkgVersionI, NULL);
satisfies = SomeRequirementSatisfied(pkgVersionI, reqc, reqv);
if (statePtr->reqc != 0) {
CheckVersionAndConvert(interp, pkgPtr->version, &statePtr->pkgVersionI, NULL);
satisfies = SomeRequirementSatisfied(statePtr->pkgVersionI, statePtr->reqc, reqv);
ckfree(pkgVersionI);
ckfree(statePtr->pkgVersionI);
if (!satisfies) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"version conflict for package \"%s\": have %s, need",
name, pkgPtr->version));
statePtr->name, pkgPtr->version));
Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "VERSIONCONFLICT",
NULL);
AddRequirementsToResult(interp, reqc, reqv);
return NULL;
AddRequirementsToResult(interp, statePtr->reqc, reqv);
return TCL_ERROR;
}
}
if (clientDataPtr) {
const void **ptr = (const void **) clientDataPtr;
if (statePtr->clientDataPtr) {
const void **ptr = (const void **) statePtr->clientDataPtr;
*ptr = pkgPtr->clientData;
}
return pkgPtr->version;
statePtr->result = pkgPtr->version;
return TCL_OK;
}
int
PkgRequireCoreFinish(
ClientData data[],
Tcl_Interp *interp,
int result
) {
PkgRequireState *statePtr = data[0];
if (statePtr->result == NULL) {
result = TCL_ERROR;
} else {
Tcl_SetObjResult(interp, Tcl_NewStringObj(statePtr->result, -1));
result = TCL_OK;
}
ckfree(statePtr);
return result;
}
/*
*----------------------------------------------------------------------
@@ -756,13 +965,24 @@ Tcl_PkgPresentEx(
*/
/* ARGSUSED */
int
Tcl_PackageObjCmd(
ClientData dummy, /* Not used. */
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
Tcl_Obj *const objv[] /* Argument objects. */
) {
return Tcl_NRCallObjProc(interp, TclNRPackageObjCmd, dummy, objc, objv);
}
int
TclNRPackageObjCmd(
ClientData dummy, /* Not used. */
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const objv[] /* Argument objects. */
) {
static const char *const pkgOptions[] = {
"forget", "ifneeded", "names", "prefer", "present",
"provide", "require", "unknown", "vcompare", "versions",
@@ -976,7 +1196,7 @@ Tcl_PackageObjCmd(
}
return Tcl_PkgProvideEx(interp, argv2, argv3, NULL);
case PKG_REQUIRE:
require:
require: {
if (objc < 3) {
requireSyntax:
Tcl_WrongNumArgs(interp, 2, objv,
@@ -988,13 +1208,11 @@ Tcl_PackageObjCmd(
argv2 = TclGetString(objv[2]);
if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) {
Tcl_Obj *ov;
int res;
Tcl_Obj ** objv2;
if (objc != 5) {
goto requireSyntax;
}
objv2 = ckalloc((sizeof(Tcl_Obj *)));
version = TclGetString(objv[4]);
if (CheckVersionAndConvert(interp, version, NULL,
NULL) != TCL_OK) {
@@ -1005,22 +1223,34 @@ Tcl_PackageObjCmd(
* Create a new-style requirement for the exact version.
*/
ov = Tcl_NewStringObj(version, -1);
Tcl_AppendStringsToObj(ov, "-", version, NULL);
version = NULL;
objv2[0] = Tcl_NewStringObj(version, -1);
Tcl_IncrRefCount(objv2[0]);
Tcl_AppendStringsToObj(objv2[0], "-", version, NULL);
argv3 = TclGetString(objv[3]);
Tcl_IncrRefCount(objv[3]);
Tcl_IncrRefCount(ov);
res = Tcl_PkgRequireProc(interp, argv3, 1, &ov, NULL);
TclDecrRefCount(ov);
return res;
TclNRAddCallback(interp, TclPkgRequireProcFinish,
INT2PTR(1), objv2, objv[3], NULL);
TclNRAddCallback(interp, TclNRPkgRequireProc, argv3, 1, objv2, NULL);
return TCL_OK;
} else {
Tcl_Obj ** objv2;
if (CheckAllRequirements(interp, objc-3, objv+3) != TCL_OK) {
return TCL_ERROR;
}
objv2 = ckalloc((objc-3)*(sizeof(Tcl_Obj *)));
for (i = 3; i < objc; i++) {
objv2[i-3] = objv[i];
Tcl_IncrRefCount(objv[i]);
}
return Tcl_PkgRequireProc(interp, argv2, objc-3, objv+3, NULL);
TclNRAddCallback(interp, TclPkgRequireProcFinish,
INT2PTR(objc-3), objv2, NULL, NULL);
TclNRAddCallback(interp, TclNRPkgRequireProc, argv2,
INT2PTR(objc-3), objv2, NULL);
return TCL_OK;
}
}
break;
case PKG_UNKNOWN: {
int length;
+83 -2
View File
@@ -379,6 +379,7 @@ Tcl_NewUnicodeObj(
Tcl_Obj *objPtr;
TclNewObj(objPtr);
TclStringSetPrev(objPtr, NULL);
SetUnicodeObj(objPtr, unicode, numChars);
return objPtr;
}
@@ -421,6 +422,7 @@ Tcl_GetCharLength(
return length;
}
/*
* OK, need to work with the object as a string.
*/
@@ -439,6 +441,50 @@ Tcl_GetCharLength(
}
return numChars;
}
/*
*----------------------------------------------------------------------
*
* TclCheckEmptyString --
*
* Determine whether the string value of an object is or would be the
* empty string, without generating a string representation.
*
* Results:
* Returns 1 if empty, 0 if not, and -1 if unknown.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TclCheckEmptyString (
Tcl_Obj *objPtr
) {
int length = -1;
if (objPtr->bytes == tclEmptyStringRep) {
return TCL_EMPTYSTRING_YES;
}
if (TclIsPureList(objPtr)) {
Tcl_ListObjLength(NULL, objPtr, &length);
return length == 0;
}
if (TclIsPureDict(objPtr)) {
Tcl_DictObjSize(NULL, objPtr, &length);
return length == 0;
}
if (objPtr->bytes == NULL) {
return TCL_EMPTYSTRING_UNKNOWN;
}
return objPtr->length == 0;
}
/*
*----------------------------------------------------------------------
@@ -942,11 +988,17 @@ Tcl_SetUnicodeObj(
int numChars) /* Number of characters in the unicode
* string. */
{
Tcl_Obj *prevPtr = NULL;
if (Tcl_IsShared(objPtr)) {
Tcl_Panic("%s called with shared object", "Tcl_SetUnicodeObj");
}
if (objPtr->typePtr != NULL && objPtr->typePtr != &tclStringType) {
prevPtr = Tcl_DuplicateObj(objPtr);
Tcl_IncrRefCount(prevPtr);
}
TclFreeIntRep(objPtr);
SetUnicodeObj(objPtr, unicode, numChars);
TclStringSetPrev(objPtr, prevPtr);
}
static int
@@ -992,7 +1044,6 @@ SetUnicodeObj(
stringPtr->unicode[numChars] = 0;
stringPtr->numChars = numChars;
stringPtr->hasUnicode = 1;
TclInvalidateStringRep(objPtr);
stringPtr->allocated = 0;
}
@@ -1328,6 +1379,9 @@ AppendUnicodeToUnicodeRep(
}
SetStringFromAny(NULL, objPtr);
if (appendNumChars > 0) {
TclStringInvalidatePrev(objPtr);
}
stringPtr = GET_STRING(objPtr);
/*
@@ -1481,6 +1535,7 @@ AppendUtfToUtfRep(
if (numBytes == 0) {
return;
}
TclStringInvalidatePrev(objPtr);
/*
* Copy the new string onto the end of the old string, then add the
@@ -2782,6 +2837,9 @@ ExtendUnicodeRepWithString(
if (numAppendChars == -1) {
TclNumUtfChars(numAppendChars, bytes, numBytes);
}
if (numAppendChars > 0) {
TclStringInvalidatePrev(objPtr);
}
needed = numOrigChars + numAppendChars;
stringCheckLimits(needed);
@@ -2829,6 +2887,7 @@ DupStringInternalRep(
{
String *srcStringPtr = GET_STRING(srcPtr);
String *copyStringPtr = NULL;
Tcl_Obj *prevPtr;
if (srcStringPtr->numChars == -1) {
/*
@@ -2870,10 +2929,15 @@ DupStringInternalRep(
* code, so it doesn't contain any extra bytes that might exist in the
* source object.
*/
copyStringPtr->allocated = copyPtr->bytes ? copyPtr->length : 0;
SET_STRING(copyPtr, copyStringPtr);
prevPtr = TclStringGetPrev(srcPtr);
if (prevPtr != NULL) {
prevPtr = Tcl_DuplicateObj(prevPtr);
Tcl_IncrRefCount(prevPtr);
}
TclStringSetPrev(copyPtr, prevPtr);
copyPtr->typePtr = &tclStringType;
}
@@ -2901,6 +2965,8 @@ SetStringFromAny(
{
if (objPtr->typePtr != &tclStringType) {
String *stringPtr = stringAlloc(0);
Tcl_Obj *prevPtr = Tcl_DuplicateObj(objPtr);
Tcl_IncrRefCount(prevPtr);
/*
* Convert whatever we have into an untyped value. Just A String.
@@ -2919,6 +2985,7 @@ SetStringFromAny(
stringPtr->maxChars = 0;
stringPtr->hasUnicode = 0;
SET_STRING(objPtr, stringPtr);
TclStringSetPrev(objPtr, prevPtr);
objPtr->typePtr = &tclStringType;
}
return TCL_OK;
@@ -2977,6 +3044,7 @@ ExtendStringRepWithUnicode(
if (numChars == 0) {
return 0;
}
TclStringInvalidatePrev(objPtr);
if (objPtr->bytes == NULL) {
objPtr->length = 0;
@@ -3038,10 +3106,23 @@ static void
FreeStringInternalRep(
Tcl_Obj *objPtr) /* Object with internal rep to free. */
{
Tcl_Obj * prevPtr = TclStringGetPrev(objPtr);
if (prevPtr != NULL) {
Tcl_DecrRefCount(prevPtr);
}
ckfree(GET_STRING(objPtr));
objPtr->typePtr = NULL;
}
static void InvalidateCachedReps(
Tcl_Obj *objPtr
) {
Tcl_Obj * prevPtr = TclStringGetPrev(objPtr);
if (prevPtr != NULL) {
Tcl_DecrRefCount(TclStringGetPrev(objPtr));
}
}
/*
* Local Variables:
* mode: c
+1
View File
@@ -624,6 +624,7 @@ TclThreadFreeObj(
GETCACHE(cachePtr);
/*
* Get this thread's list and push on the free Tcl_Obj.
*/
+2 -2
View File
@@ -228,8 +228,8 @@ test load-9.1 {Tcl_StaticPackage, load already-loaded package into another inter
test load-10.1 {load from vfs} \
-constraints [list $dll $loaded testsimplefilesystem] \
-setup {set dir [pwd]; cd $testDir; testsimplefilesystem 1} \
-body {list [catch {load simplefs:/pkgd$ext pkgd} msg] $msg} \
-result {0 {}} \
-body {list [catch {load simplefs:/pkgd$ext pkgd} msg]} \
-result 0 \
-cleanup {testsimplefilesystem 0; cd $dir; unset dir}
test load-11.1 {Load TclOO extension using Stubs (Bug [f51efe99a7])} \
+12
View File
@@ -605,6 +605,18 @@ test package-3.52 {Tcl_PkgRequire procedure, picking best stable version} -setup
package require t
return $x
} -result {1.3}
test package-3.53 {Tcl_PkgRequire procedure, coroutine support} -setup {
package forget t
} -body {
coroutine coro1 apply {{} {
package ifneeded t 2.1 {
yield
package provide t 2.1
}
package require t 2.1
}}
list [catch {coro1} msg] $msg
} -match glob -result {0 2.1}
test package-4.1 {Tcl_PackageCmd procedure} -returnCodes error -body {
package
+3 -3
View File
@@ -86,7 +86,7 @@ test unload-2.1 {basic loading of non-unloadable package, with guess for package
} {0 {pkga_eq pkga_quote}}
test unload-2.2 {basic loading of unloadable package, with guess for package name} [list $dll $loaded] {
list $pkgua_loaded $pkgua_detached $pkgua_unloaded \
[load [file join $testDir pkgua$ext]] \
[load [file join $testDir pkgua$ext]; list] \
[pkgua_eq abc def] [lsort [info commands pkgua_*]] \
$pkgua_loaded $pkgua_detached $pkgua_unloaded
} {{} {} {} {} 0 {pkgua_eq pkgua_quote} . {} {}}
@@ -110,7 +110,7 @@ test unload-2.5 {reloading of unloaded package, with guess for package name} -se
}
} -constraints [list $dll $loaded] -body {
list $pkgua_loaded $pkgua_detached $pkgua_unloaded \
[load [file join $testDir pkgua$ext]] \
[load [file join $testDir pkgua$ext]; list] \
[pkgua_eq abc def] [lsort [info commands pkgua_*]] \
$pkgua_loaded $pkgua_detached $pkgua_unloaded
} -result {. . . {} 0 {pkgua_eq pkgua_quote} .. . .}
@@ -213,7 +213,7 @@ test unload-4.1 {loading of unloadable package in trusted interpreter, with gues
incr load(M)
} -constraints [list $dll $loaded] -body {
list [list $pkgua_loaded $pkgua_detached $pkgua_unloaded] \
[load [file join $testDir pkgua$ext]] \
[load [file join $testDir pkgua$ext]; list] \
[pkgua_eq abc def] [lsort [info commands pkgua_*]] \
[list $pkgua_loaded $pkgua_detached $pkgua_unloaded]
} -result {{{} {} {}} {} 0 {pkgua_eq pkgua_quote} {. {} {}}}
+1
View File
@@ -718,6 +718,7 @@ gdb-test: ${TCLTEST_EXE}
@echo "set env @LD_LIBRARY_PATH_VAR@=`pwd`:$${@LD_LIBRARY_PATH_VAR@}" > gdb.run
@echo "set env TCL_LIBRARY=${TCL_BUILDTIME_LIBRARY}" >> gdb.run
@echo "set args $(TOP_DIR)/tests/all.tcl $(TESTFLAGS) -singleproc 1" >> gdb.run
@echo "$(GDBSCRIPT)" >> gdb.run
$(GDB) ./${TCLTEST_EXE} --command=gdb.run
rm gdb.run
+1 -1
View File
@@ -734,7 +734,7 @@ AC_DEFUN([SC_ENABLE_SYMBOLS], [
AC_MSG_CHECKING([for build with symbols])
AC_ARG_ENABLE(symbols,
AC_HELP_STRING([--enable-symbols],
[build with debugging symbols (default: off)]),
[build with debugging symbols (all, compile, or mem) (default: off)]),
[tcl_ok=$enableval], [tcl_ok=no])
# FIXME: Currently, LDFLAGS_DEFAULT is not used, it should work like CFLAGS_DEFAULT.
DBGX=""