0001: /*
0002: * ------------------------------------------------------------------------
0003: * PACKAGE: [incr Tcl]
0004: * DESCRIPTION: Object-Oriented Extensions to Tcl
0005: *
0006: * [incr Tcl] provides object-oriented extensions to Tcl, much as
0007: * C++ provides object-oriented extensions to C. It provides a means
0008: * of encapsulating related procedures together with their shared data
0009: * in a local namespace that is hidden from the outside world. It
0010: * promotes code re-use through inheritance. More than anything else,
0011: * it encourages better organization of Tcl applications through the
0012: * object-oriented paradigm, leading to code that is easier to
0013: * understand and maintain.
0014: *
0015: * This segment provides common utility functions used throughout
0016: * the other [incr Tcl] source files.
0017: *
0018: * ========================================================================
0019: * AUTHOR: Michael J. McLennan
0020: * Bell Labs Innovations for Lucent Technologies
0021: * mmclennan@lucent.com
0022: * http://www.tcltk.com/itcl
0023: *
0024: * RCS: $Id: Util.java,v 1.2 2005/09/12 00:00:50 mdejong Exp $
0025: * ========================================================================
0026: * Copyright (c) 1993-1998 Lucent Technologies, Inc.
0027: * ------------------------------------------------------------------------
0028: * See the file "license.itcl" for information on usage and redistribution
0029: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
0030: */
0031:
0032: package itcl.lang;
0033:
0034: import java.util.Hashtable;
0035: import java.util.Stack;
0036:
0037: import tcl.lang.*;
0038:
0039: // These records are used to keep track of reference-counted data
0040: // for Itcl_PreserveData and Itcl_ReleaseData.
0041:
0042: class ItclPreservedData {
0043: int usage; // number of active uses
0044: ItclEventuallyFreed fobj; // Object to be freed eventually
0045: }
0046:
0047: interface ItclEventuallyFreed {
0048: // Invoked when data is no longer being used
0049: void eventuallyFreed();
0050: }
0051:
0052: // This structure is used to take a snapshot of the interpreter
0053: // state in Itcl_SaveInterpState. You can snapshot the state,
0054: // execute a command, and then back up to the result or the
0055: // error that was previously in progress.
0056:
0057: class Itcl_InterpState {
0058: int validate; // validation stamp
0059: int status; // return code status
0060: TclObject objResult; // result object
0061: TclObject errorInfo; // contents of errorInfo variable
0062: TclObject errorCode; // contents of errorCode variable
0063: }
0064:
0065: class Util {
0066:
0067: // POOL OF LIST ELEMENTS FOR LINKED LIST
0068:
0069: static Itcl_ListElem listPool = null;
0070: static int listPoolLen = 0;
0071:
0072: static Hashtable ItclPreservedList = new Hashtable();
0073: // Mutex ItclPreservedListLock
0074:
0075: static int VALID_LIST = 0x01face10; // magic bit pattern for validation
0076: static int LIST_POOL_SIZE = 200; // max number of elements in listPool
0077:
0078: static int STATE_VALID = 0x01233210; // magic bit pattern for validation
0079:
0080: /*
0081: * ------------------------------------------------------------------------
0082: * Itcl_Assert -> Util.Assert
0083: *
0084: * Called to mimic an assert() statement in C. Raises a TclRuntimeError
0085: * if the test expression evaluates to false.
0086: * ------------------------------------------------------------------------
0087: */
0088:
0089: static void Assert(boolean tExpr, // test expression
0090: String tExprStr) // string representing test expression
0091: {
0092: if (!tExpr)
0093: throw new TclRuntimeError("Itcl Assertion failed: \""
0094: + tExprStr + "\"");
0095: }
0096:
0097: /*
0098: * ------------------------------------------------------------------------
0099: * Itcl_InitStack -> Util.InitStack
0100: *
0101: * Initializes a stack structure, allocating a certain amount of memory
0102: * for the stack and setting the stack length to zero.
0103: * ------------------------------------------------------------------------
0104: */
0105:
0106: static void InitStack(Itcl_Stack stack) // stack to be initialized
0107: {
0108: stack.s = new Stack();
0109: }
0110:
0111: /*
0112: * ------------------------------------------------------------------------
0113: * Itcl_DeleteStack -> Util.DeleteStack
0114: *
0115: * Destroys a stack structure, freeing any memory that may have been
0116: * allocated to represent it.
0117: * ------------------------------------------------------------------------
0118: */
0119:
0120: static void DeleteStack(Itcl_Stack stack) // stack to be deleted
0121: {
0122: stack.s.clear();
0123: stack.s = null;
0124: }
0125:
0126: /*
0127: * ------------------------------------------------------------------------
0128: * Itcl_PushStack -> Util.PushStack
0129: *
0130: * Pushes a piece of client data onto the top of the given stack.
0131: * If the stack is not large enough, it is automatically resized.
0132: * ------------------------------------------------------------------------
0133: */
0134:
0135: static void PushStack(Object cdata, // data to be pushed onto stack
0136: Itcl_Stack stack) // stack
0137: {
0138: stack.s.push(cdata);
0139: }
0140:
0141: /*
0142: * ------------------------------------------------------------------------
0143: * Itcl_PopStack -> Util.PopStack
0144: *
0145: * Pops a bit of client data from the top of the given stack.
0146: * ------------------------------------------------------------------------
0147: */
0148:
0149: static Object PopStack(Itcl_Stack stack) // stack to be manipulated
0150: {
0151: if (stack.s != null && !stack.s.empty()) {
0152: return stack.s.pop();
0153: }
0154: return null;
0155: }
0156:
0157: /*
0158: * ------------------------------------------------------------------------
0159: * Itcl_PeekStack -> Util.PeekStack
0160: *
0161: * Gets the current value from the top of the given stack.
0162: * ------------------------------------------------------------------------
0163: */
0164:
0165: static Object PeekStack(Itcl_Stack stack) // stack to be examined
0166: {
0167: if (stack.s == null)
0168: return null;
0169: return stack.s.peek();
0170: }
0171:
0172: /*
0173: * ------------------------------------------------------------------------
0174: * Itcl_GetStackValue -> Util.GetStackValue
0175: *
0176: * Gets a value at some index within the stack. Index "0" is the
0177: * first value pushed onto the stack.
0178: * ------------------------------------------------------------------------
0179: */
0180:
0181: static Object GetStackValue(Itcl_Stack stack, // stack to be examined
0182: int pos) // get value at this index
0183: {
0184: if (stack.s == null)
0185: return null;
0186: return stack.s.elementAt(pos);
0187: }
0188:
0189: /*
0190: * ------------------------------------------------------------------------
0191: * Itcl_GetStackSize -> Util.GetStackSize
0192: *
0193: * Gets the number of elements in the stack.
0194: * ------------------------------------------------------------------------
0195: */
0196:
0197: static int GetStackSize(Itcl_Stack stack) // stack
0198: {
0199: if (stack.s == null)
0200: return 0;
0201: return stack.s.size();
0202: }
0203:
0204: /*
0205: * ------------------------------------------------------------------------
0206: * Itcl_InitList -> Util.InitList
0207: *
0208: * Initializes a linked list structure, setting the list to the empty
0209: * state.
0210: * ------------------------------------------------------------------------
0211: */
0212:
0213: static void InitList(Itcl_List list) // list to be initialized
0214: {
0215: list.validate = Util.VALID_LIST;
0216: list.num = 0;
0217: list.head = null;
0218: list.tail = null;
0219: }
0220:
0221: /*
0222: * ------------------------------------------------------------------------
0223: * Itcl_DeleteList -> Util.DeleteList
0224: *
0225: * Destroys a linked list structure, deleting all of its elements and
0226: * setting it to an empty state. If the elements have memory associated
0227: * with them, this memory must be freed before deleting the list or it
0228: * will be lost.
0229: * ------------------------------------------------------------------------
0230: */
0231:
0232: static void DeleteList(Itcl_List list) // list to be deleted
0233: {
0234: Itcl_ListElem elem;
0235:
0236: Assert(list.validate == Util.VALID_LIST,
0237: "list.validate == Util.VALID_LIST");
0238:
0239: elem = list.head;
0240: while (elem != null) {
0241: elem = Util.DeleteListElem(elem);
0242: }
0243: list.validate = 0;
0244: }
0245:
0246: /*
0247: * ------------------------------------------------------------------------
0248: * Itcl_CreateListElem -> Util.CreateListElem
0249: *
0250: * Low-level routined used by procedures like InsertList() and
0251: * AppendList() to create new list elements. If elements are
0252: * available, one is taken from the list element pool. Otherwise,
0253: * a new one is allocated.
0254: * ------------------------------------------------------------------------
0255: */
0256:
0257: static Itcl_ListElem CreateListElem(Itcl_List list) // list that will contain this new element
0258: {
0259: Itcl_ListElem elem;
0260:
0261: if (listPoolLen > 0) {
0262: elem = listPool;
0263: listPool = elem.next;
0264: --listPoolLen;
0265: } else {
0266: elem = new Itcl_ListElem();
0267: }
0268: elem.owner = list;
0269: elem.value = null;
0270: elem.next = null;
0271: elem.prev = null;
0272:
0273: return elem;
0274: }
0275:
0276: /*
0277: * ------------------------------------------------------------------------
0278: * Itcl_DeleteListElem -> Util.DeleteListElem
0279: *
0280: * Destroys a single element in a linked list, returning it to a pool of
0281: * elements that can be later reused. Returns a pointer to the next
0282: * element in the list.
0283: * ------------------------------------------------------------------------
0284: */
0285:
0286: static Itcl_ListElem DeleteListElem(Itcl_ListElem elem) // list element to be deleted
0287: {
0288: Itcl_List list;
0289: Itcl_ListElem next;
0290:
0291: next = elem.next;
0292:
0293: if (elem.prev != null) {
0294: elem.prev.next = elem.next;
0295: }
0296: if (elem.next != null) {
0297: elem.next.prev = elem.prev;
0298: }
0299:
0300: list = elem.owner;
0301: if (elem == list.head)
0302: list.head = elem.next;
0303: if (elem == list.tail)
0304: list.tail = elem.prev;
0305: --list.num;
0306:
0307: if (listPoolLen < Util.LIST_POOL_SIZE) {
0308: elem.next = listPool;
0309: listPool = elem;
0310: ++listPoolLen;
0311: } else {
0312: elem = null;
0313: }
0314: return next;
0315: }
0316:
0317: /*
0318: * ------------------------------------------------------------------------
0319: * Itcl_InsertList -> Util.InsertList
0320: *
0321: * Creates a new list element containing the given value and returns
0322: * a pointer to it. The element is inserted at the beginning of the
0323: * specified list.
0324: * ------------------------------------------------------------------------
0325: */
0326:
0327: static Itcl_ListElem InsertList(Itcl_List list, // list being modified
0328: Object val) // value associated with new element
0329: {
0330: Itcl_ListElem elem;
0331: Assert(list.validate == Util.VALID_LIST,
0332: "list.validate == Util.VALID_LIST");
0333:
0334: elem = Util.CreateListElem(list);
0335:
0336: elem.value = val;
0337: elem.next = list.head;
0338: elem.prev = null;
0339: if (list.head != null) {
0340: list.head.prev = elem;
0341: }
0342: list.head = elem;
0343: if (list.tail == null) {
0344: list.tail = elem;
0345: }
0346: ++list.num;
0347:
0348: return elem;
0349: }
0350:
0351: /*
0352: * ------------------------------------------------------------------------
0353: * Itcl_InsertListElem -> Util.InsertListElem
0354: *
0355: * Creates a new list element containing the given value and returns
0356: * a pointer to it. The element is inserted in the list just before
0357: * the specified element.
0358: * ------------------------------------------------------------------------
0359: */
0360:
0361: static Itcl_ListElem InsertListElem(Itcl_ListElem pos, // insert just before this element
0362: Object val) // value associated with new element
0363: {
0364: Itcl_List list;
0365: Itcl_ListElem elem;
0366:
0367: list = pos.owner;
0368: Assert(list.validate == Util.VALID_LIST,
0369: "list.validate == Util.VALID_LIST");
0370: // Next assert makes no sense as pos was already accessed above
0371: //Assert(pos != null,
0372: // "pos != null");
0373:
0374: elem = Util.CreateListElem(list);
0375: elem.value = val;
0376:
0377: elem.prev = pos.prev;
0378: if (elem.prev != null) {
0379: elem.prev.next = elem;
0380: }
0381: elem.next = pos;
0382: pos.prev = elem;
0383:
0384: if (list.head == pos) {
0385: list.head = elem;
0386: }
0387: if (list.tail == null) {
0388: list.tail = elem;
0389: }
0390: ++list.num;
0391:
0392: return elem;
0393: }
0394:
0395: /*
0396: * ------------------------------------------------------------------------
0397: * Itcl_AppendList -> Util.AppendList
0398: *
0399: * Creates a new list element containing the given value and returns
0400: * a pointer to it. The element is appended at the end of the
0401: * specified list.
0402: * ------------------------------------------------------------------------
0403: */
0404:
0405: static Itcl_ListElem AppendList(Itcl_List list, // list being modified
0406: Object val) // value associated with new element
0407: {
0408: Itcl_ListElem elem;
0409: Assert(list.validate == Util.VALID_LIST,
0410: "list.validate == Util.VALID_LIST");
0411:
0412: elem = Util.CreateListElem(list);
0413:
0414: elem.value = val;
0415: elem.prev = list.tail;
0416: elem.next = null;
0417: if (list.tail != null) {
0418: list.tail.next = elem;
0419: }
0420: list.tail = elem;
0421: if (list.head == null) {
0422: list.head = elem;
0423: }
0424: ++list.num;
0425:
0426: return elem;
0427: }
0428:
0429: /*
0430: * ------------------------------------------------------------------------
0431: * Itcl_AppendListElem -> Util.AppendListElem
0432: *
0433: * Creates a new list element containing the given value and returns
0434: * a pointer to it. The element is inserted in the list just after
0435: * the specified element.
0436: * ------------------------------------------------------------------------
0437: */
0438:
0439: static Itcl_ListElem AppendListElem(Itcl_ListElem pos, // insert just after this element
0440: Object val) // value associated with new element
0441: {
0442: Itcl_List list;
0443: Itcl_ListElem elem;
0444:
0445: list = pos.owner;
0446: Assert(list.validate == Util.VALID_LIST,
0447: "list.validate == Util.VALID_LIST");
0448: // Next assert makes no sense as pos was already accessed above
0449: //Assert(pos != null,
0450: // "pos != null");
0451:
0452: elem = Util.CreateListElem(list);
0453: elem.value = val;
0454:
0455: elem.next = pos.next;
0456: if (elem.next != null) {
0457: elem.next.prev = elem;
0458: }
0459: elem.prev = pos;
0460: pos.next = elem;
0461:
0462: if (list.tail == pos) {
0463: list.tail = elem;
0464: }
0465: if (list.head == null) {
0466: list.head = elem;
0467: }
0468: ++list.num;
0469:
0470: return elem;
0471: }
0472:
0473: /*
0474: * ------------------------------------------------------------------------
0475: * Itcl_SetListValue -> Util.SetListValue
0476: *
0477: * Modifies the value associated with a list element.
0478: * ------------------------------------------------------------------------
0479: */
0480:
0481: static void SetListValue(Itcl_ListElem elem, // list element being modified
0482: Object val) // new value associated with element
0483: {
0484: Itcl_List list = elem.owner;
0485: Assert(list.validate == Util.VALID_LIST,
0486: "list.validate == Util.VALID_LIST");
0487: Assert(elem != null, "elem != null");
0488:
0489: elem.value = val;
0490: }
0491:
0492: /*
0493: * ------------------------------------------------------------------------
0494: * Itcl_NextListElem -> Util.NextListElem
0495: *
0496: * Returns the next list element.
0497: * ------------------------------------------------------------------------
0498: */
0499:
0500: static Itcl_ListElem NextListElem(Itcl_ListElem elem) {
0501: return elem.next;
0502: }
0503:
0504: /*
0505: * ------------------------------------------------------------------------
0506: * Itcl_PrevListElem -> Util.PrevListElem
0507: *
0508: * Returns the prev list element.
0509: * ------------------------------------------------------------------------
0510: */
0511:
0512: static Itcl_ListElem PrevListElem(Itcl_ListElem elem) {
0513: return elem.prev;
0514: }
0515:
0516: /*
0517: * ------------------------------------------------------------------------
0518: * Itcl_FirstListElem -> Util.FirstListElem
0519: *
0520: * Returns the first list element.
0521: * ------------------------------------------------------------------------
0522: */
0523:
0524: static Itcl_ListElem FirstListElem(Itcl_List l) {
0525: return l.head;
0526: }
0527:
0528: /*
0529: * ------------------------------------------------------------------------
0530: * Itcl_LastListElem -> Util.LastListElem
0531: *
0532: * Returns the last list element.
0533: * ------------------------------------------------------------------------
0534: */
0535:
0536: static Itcl_ListElem LastListElem(Itcl_List l) {
0537: return l.tail;
0538: }
0539:
0540: /*
0541: * ------------------------------------------------------------------------
0542: * Itcl_GetListLength -> Util.GetListLength
0543: *
0544: * Returns the list length.
0545: * ------------------------------------------------------------------------
0546: */
0547:
0548: static int GetListLength(Itcl_List l) {
0549: return l.num;
0550: }
0551:
0552: /*
0553: * ------------------------------------------------------------------------
0554: * Itcl_GetListValue -> Util.GetListValue
0555: *
0556: * Get the list element value.
0557: * ------------------------------------------------------------------------
0558: */
0559:
0560: static Object GetListValue(Itcl_ListElem elem) {
0561: return elem.value;
0562: }
0563:
0564: /*
0565: * ========================================================================
0566: * REFERENCE-COUNTED DATA
0567: *
0568: * The following procedures manage generic reference-counted data.
0569: * They are similar in spirit to the Tcl_Preserve/Tcl_Release
0570: * procedures defined in the Tcl/Tk core. But these procedures use
0571: * a hash table instead of a linked list to maintain the references,
0572: * so they scale better. Also, the Tcl procedures have a bad behavior
0573: * during the "exit" command. Their exit handler shuts them down
0574: * when other data is still being reference-counted and cleaned up.
0575: *
0576: * ------------------------------------------------------------------------
0577: * Itcl_EventuallyFree -> Util.EventuallyFree
0578: *
0579: * Registers a piece of data so that it will be freed when no longer
0580: * in use. The data is registered with an initial usage count of "0".
0581: * Future calls to Itcl_PreserveData() increase this usage count, and
0582: * calls to Itcl_ReleaseData() decrease the count until it reaches
0583: * zero and the data is freed.
0584: * ------------------------------------------------------------------------
0585: */
0586:
0587: static void EventuallyFree(ItclEventuallyFreed fobj) // Object to be freed eventually
0588: {
0589: // No-op since Util.PreserveData() does everything we need.
0590: return;
0591: }
0592:
0593: /*
0594: * ------------------------------------------------------------------------
0595: * Itcl_PreserveData -> Util.PreserveData
0596: *
0597: * Increases the usage count for a piece of data that will be freed
0598: * later when no longer needed. Each call to Itcl_PreserveData()
0599: * puts one claim on a piece of data, and subsequent calls to
0600: * Itcl_ReleaseData() remove those claims. When Itcl_EventuallyFree()
0601: * is called, and when the usage count reaches zero, the data is
0602: * freed.
0603: * ------------------------------------------------------------------------
0604: */
0605:
0606: static void PreserveData(ItclEventuallyFreed fobj) // data to be preserved
0607: {
0608: ItclPreservedData chunk;
0609:
0610: // If the fobj value is null, do nothing.
0611:
0612: if (fobj == null) {
0613: return;
0614: }
0615:
0616: // ItclPreservedList already intialized so that it
0617: // can be used as a monitor.
0618:
0619: synchronized (ItclPreservedList) {
0620:
0621: // Find the data in the global list and bump its usage count.
0622:
0623: chunk = (ItclPreservedData) ItclPreservedList.get(fobj);
0624: if (chunk == null) {
0625: chunk = new ItclPreservedData();
0626: chunk.fobj = fobj;
0627: chunk.usage = 0;
0628: ItclPreservedList.put(fobj, chunk);
0629: } else {
0630: // No-op
0631: }
0632:
0633: // Only increment the usage if it is non-negative.
0634: // Negative numbers mean that the data is in the process
0635: // of being destroyed by Itcl_ReleaseData(), and should
0636: // not be further preserved.
0637:
0638: if (chunk.usage >= 0) {
0639: chunk.usage++;
0640: }
0641:
0642: } // end synchronized block
0643: }
0644:
0645: /*
0646: * ------------------------------------------------------------------------
0647: * Itcl_ReleaseData -> Util.ReleaseData
0648: *
0649: * Decreases the usage count for a piece of data that was registered
0650: * previously via Itcl_PreserveData(). After Itcl_EventuallyFree()
0651: * is called and the usage count reaches zero, the data is
0652: * automatically freed.
0653: * ------------------------------------------------------------------------
0654: */
0655:
0656: static void ReleaseData(ItclEventuallyFreed fobj) // data to be released
0657: {
0658: ItclPreservedData chunk;
0659: boolean delEntry = false;
0660:
0661: // If the fobj value is null, do nothing.
0662:
0663: if (fobj == null) {
0664: return;
0665: }
0666:
0667: // Otherwise, find the data in the global list and
0668: // decrement its usage count.
0669:
0670: synchronized (ItclPreservedList) {
0671:
0672: chunk = (ItclPreservedData) ItclPreservedList.get(fobj);
0673:
0674: Assert(chunk != null, "chunk != null");
0675:
0676: // Only decrement the usage if it is non-negative.
0677: // When the usage reaches zero, set it to a negative number
0678: // to indicate that data is being destroyed, and then
0679: // invoke the client delete proc. When the data is deleted,
0680: // remove the entry from the preservation list.
0681:
0682: if (chunk.usage > 0 && --chunk.usage == 0) {
0683: chunk.usage = -1; // cannot preserve/release anymore
0684: delEntry = true;
0685: }
0686:
0687: } // end synchronized block
0688:
0689: if (delEntry) {
0690: fobj.eventuallyFreed();
0691:
0692: synchronized (ItclPreservedList) {
0693: ItclPreservedList.remove(fobj);
0694: } // end synchronized block
0695: }
0696: }
0697:
0698: /*
0699: * ------------------------------------------------------------------------
0700: * Itcl_SaveInterpState -> Util.SaveInterpState
0701: *
0702: * Takes a snapshot of the current result state of the interpreter.
0703: * The snapshot can be restored at any point by Itcl_RestoreInterpState.
0704: * So if you are in the middle of building a return result, you can
0705: * snapshot the interpreter, execute a command that might generate an
0706: * error, restore the snapshot, and continue building the result string.
0707: *
0708: * Once a snapshot is saved, it must be restored by calling
0709: * Itcl_RestoreInterpState, or discarded by calling
0710: * Itcl_DiscardInterpState. Otherwise, memory will be leaked.
0711: *
0712: * Returns a token representing the state of the interpreter.
0713: * ------------------------------------------------------------------------
0714: */
0715:
0716: static Itcl_InterpState SaveInterpState(Interp interp, // interpreter being modified
0717: int status) // integer status code for current operation
0718: {
0719: Itcl_InterpState info;
0720: TclObject val = null;
0721:
0722: info = new Itcl_InterpState();
0723: info.validate = Util.STATE_VALID;
0724: info.status = status;
0725: info.errorInfo = null;
0726: info.errorCode = null;
0727:
0728: // Get the result object from the interpreter. This synchronizes
0729: // the old-style result, so we don't have to worry about it.
0730: // Keeping the object result is enough.
0731:
0732: info.objResult = interp.getResult();
0733: info.objResult.preserve();
0734:
0735: // If an error is in progress, preserve its state.
0736:
0737: try {
0738: val = null;
0739: val = interp.getVar("errorInfo", TCL.GLOBAL_ONLY);
0740: } catch (TclException ex) {
0741: }
0742: if (val != null && (val.toString().length() > 0)) {
0743: info.errorInfo = val;
0744: info.errorInfo.preserve();
0745: }
0746:
0747: try {
0748: val = null;
0749: val = interp.getVar("errorCode", TCL.GLOBAL_ONLY);
0750: } catch (TclException ex) {
0751: }
0752: if (val != null && (val.toString().length() > 0)) {
0753: info.errorCode = val;
0754: info.errorCode.preserve();
0755: }
0756:
0757: // Now, reset the interpreter to a clean state.
0758:
0759: interp.resetResult();
0760:
0761: return info;
0762: }
0763:
0764: /*
0765: * ------------------------------------------------------------------------
0766: * Itcl_RestoreInterpState -> Util.RestoreInterpState
0767: *
0768: * Restores the state of the interpreter to a snapshot taken by
0769: * Itcl_SaveInterpState. This affects variables such as "errorInfo"
0770: * and "errorCode". After this call, the token for the interpreter
0771: * state is no longer valid.
0772: *
0773: * Returns the status code that was pending at the time the state was
0774: * captured.
0775: * ------------------------------------------------------------------------
0776: */
0777:
0778: static int RestoreInterpState(Interp interp, // interpreter being modified
0779: Itcl_InterpState state) // token representing interpreter state
0780: {
0781: Itcl_InterpState info = state;
0782: int status;
0783:
0784: Assert(info.validate == Util.STATE_VALID,
0785: "info.validate == Util.STATE_VALID");
0786:
0787: interp.resetResult();
0788:
0789: // If an error is in progress, restore its state.
0790: // Set the error code the hard way--set the variable directly
0791: // and fix the interpreter flags. Otherwise, if the error code
0792: // string is really a list, it will get wrapped in extra {}'s.
0793:
0794: if (info.errorInfo != null) {
0795: interp.addErrorInfo(info.errorInfo.toString());
0796: info.errorInfo.release();
0797: info.errorInfo = null;
0798: }
0799:
0800: if (info.errorCode != null) {
0801: interp.setErrorCode(info.errorCode);
0802: info.errorCode.release();
0803: info.errorCode = null;
0804: }
0805:
0806: // Assign the object result back to the interpreter, then
0807: // release our hold on it.
0808:
0809: interp.setResult(info.objResult);
0810: info.objResult.release();
0811: info.objResult = null;
0812:
0813: status = info.status;
0814: info.validate = 0;
0815: info = null;
0816:
0817: return status;
0818: }
0819:
0820: /*
0821: * ------------------------------------------------------------------------
0822: * Itcl_DiscardInterpState -> Util.DiscardInterpState
0823: *
0824: * Frees the memory associated with an interpreter snapshot taken by
0825: * Itcl_SaveInterpState. If the snapshot is not restored, this
0826: * procedure must be called to discard it, or the memory will be lost.
0827: * After this call, the token for the interpreter state is no longer
0828: * valid.
0829: * ------------------------------------------------------------------------
0830: */
0831:
0832: static void DiscardInterpState(Itcl_InterpState state) // token representing interpreter state
0833: {
0834: Itcl_InterpState info = state;
0835:
0836: Assert(info.validate == Util.STATE_VALID,
0837: "info.validate == Util.STATE_VALID");
0838:
0839: if (info.errorInfo != null) {
0840: info.errorInfo.release();
0841: info.errorInfo = null;
0842: }
0843: if (info.errorCode != null) {
0844: info.errorCode.release();
0845: info.errorCode = null;
0846: }
0847: info.objResult.release();
0848: info.objResult = null;
0849:
0850: info.validate = 0;
0851: info = null;
0852: }
0853:
0854: /*
0855: * ------------------------------------------------------------------------
0856: * Itcl_Protection -> Util.Protection
0857: *
0858: * Used to query/set the protection level used when commands/variables
0859: * are defined within a class. The default protection level (when
0860: * no public/protected/private command is active) is ITCL_DEFAULT_PROTECT.
0861: * In the default case, new commands are treated as public, while new
0862: * variables are treated as protected.
0863: *
0864: * If the specified level is 0, then this procedure returns the
0865: * current value without changing it. Otherwise, it sets the current
0866: * value to the specified protection level, and returns the previous
0867: * value.
0868: * ------------------------------------------------------------------------
0869: */
0870:
0871: static int Protection(Interp interp, // interpreter being queried
0872: int newLevel) // new protection level or 0
0873: {
0874: int oldVal;
0875: ItclObjectInfo info;
0876:
0877: // If a new level was specified, then set the protection level.
0878: // In any case, return the protection level as it stands right now.
0879:
0880: info = (ItclObjectInfo) interp
0881: .getAssocData(ItclInt.INTERP_DATA);
0882:
0883: Assert(info != null, "info != null");
0884: oldVal = info.protection;
0885:
0886: if (newLevel != 0) {
0887: Assert(newLevel == Itcl.PUBLIC
0888: || newLevel == Itcl.PROTECTED
0889: || newLevel == Itcl.PRIVATE
0890: || newLevel == Itcl.DEFAULT_PROTECT,
0891: "newLevel Protection");
0892: info.protection = newLevel;
0893: }
0894: return oldVal;
0895: }
0896:
0897: /*
0898: * ------------------------------------------------------------------------
0899: * Itcl_ProtectionStr -> Util.ProtectionStr
0900: *
0901: * Converts an integer protection code (ITCL.PUBLIC, ITCL.PROTECTED,
0902: * or ITCL.PRIVATE) into a human-readable character string. Returns
0903: * a pointer to this string.
0904: * ------------------------------------------------------------------------
0905: */
0906:
0907: static String ProtectionStr(int pLevel) // protection level
0908: {
0909: switch (pLevel) {
0910: case Itcl.PUBLIC:
0911: return "public";
0912: case Itcl.PROTECTED:
0913: return "protected";
0914: case Itcl.PRIVATE:
0915: return "private";
0916: }
0917: return "<bad-protection-code>";
0918: }
0919:
0920: /*
0921: * ------------------------------------------------------------------------
0922: * Itcl_CanAccess -> Util.CanAccess
0923: *
0924: * Checks to see if a class member can be accessed from a particular
0925: * namespace context. Public things can always be accessed. Protected
0926: * things can be accessed if the "from" namespace appears in the
0927: * inheritance hierarchy of the class namespace. Private things
0928: * can be accessed only if the "from" namespace is the same as the
0929: * class that contains them.
0930: *
0931: * Returns true/false.
0932: * ------------------------------------------------------------------------
0933: */
0934:
0935: static boolean CanAccess(ItclMember member, // class member being tested
0936: Namespace fromNs) // namespace requesting access
0937: {
0938: ItclClass fromCd;
0939: Object entry;
0940:
0941: // If the protection level is "public" or "private", then the
0942: // answer is known immediately.
0943:
0944: if (member.protection == Itcl.PUBLIC) {
0945: return true;
0946: } else if (member.protection == Itcl.PRIVATE) {
0947: return (member.classDefn.namesp == fromNs);
0948: }
0949:
0950: // If the protection level is "protected", then check the
0951: // heritage of the namespace requesting access. If cdefnPtr
0952: // is in the heritage, then access is allowed.
0953:
0954: Assert(member.protection == Itcl.PROTECTED,
0955: "member.protection == Itcl.PROTECTED");
0956:
0957: if (Class.IsClassNamespace(fromNs)) {
0958: fromCd = Class.GetClassFromNamespace(fromNs);
0959:
0960: entry = fromCd.heritage.get(member.classDefn);
0961:
0962: if (entry != null) {
0963: return true;
0964: }
0965: }
0966: return false;
0967: }
0968:
0969: /*
0970: * ------------------------------------------------------------------------
0971: * Itcl_CanAccessFunc -> Util.CanAccessFunc
0972: *
0973: * Checks to see if a member function with the specified protection
0974: * level can be accessed from a particular namespace context. This
0975: * follows the same rules enforced by Itcl_CanAccess, but adds one
0976: * special case: If the function is a protected method, and if the
0977: * current context is a base class that has the same method, then
0978: * access is allowed.
0979: *
0980: * Returns true/false.
0981: * ------------------------------------------------------------------------
0982: */
0983:
0984: static boolean CanAccessFunc(ItclMemberFunc mfunc, // member function being tested
0985: Namespace fromNs) // namespace requesting access
0986: {
0987: ItclClass cd, fromCd;
0988: ItclMemberFunc ovlfunc;
0989: Object entry;
0990:
0991: // Apply the usual rules first.
0992:
0993: if (Util.CanAccess(mfunc.member, fromNs)) {
0994: return true;
0995: }
0996:
0997: // As a last resort, see if the namespace is really a base
0998: // class of the class containing the method. Look for a
0999: // method with the same name in the base class. If there
1000: // is one, then this method overrides it, and the base class
1001: // has access.
1002:
1003: if ((mfunc.member.flags & ItclInt.COMMON) == 0
1004: && Class.IsClassNamespace(fromNs)) {
1005:
1006: cd = mfunc.member.classDefn;
1007: fromCd = Class.GetClassFromNamespace(fromNs);
1008:
1009: if (cd.heritage.get(fromCd) != null) {
1010: entry = fromCd.resolveCmds.get(mfunc.member.name);
1011:
1012: if (entry != null) {
1013: ovlfunc = (ItclMemberFunc) entry;
1014: if ((ovlfunc.member.flags & ItclInt.COMMON) == 0
1015: && ovlfunc.member.protection < Itcl.PRIVATE) {
1016: return true;
1017: }
1018: }
1019: }
1020: }
1021: return false;
1022: }
1023:
1024: /*
1025: * ------------------------------------------------------------------------
1026: * Itcl_GetTrueNamespace -> Util.GetTrueNamespace
1027: *
1028: * Returns the current namespace context. This procedure is similar
1029: * to Tcl_GetCurrentNamespace, but it supports the notion of
1030: * "transparent" call frames installed by Itcl_HandleInstance.
1031: *
1032: * Returns a pointer to the current namespace calling context.
1033: * ------------------------------------------------------------------------
1034: */
1035:
1036: static Namespace GetTrueNamespace(Interp interp, // interpreter being queried
1037: ItclObjectInfo info) // object info associated with interp
1038: {
1039: int i;
1040: boolean transparent;
1041: CallFrame frame, transFrame;
1042: Namespace contextNs;
1043:
1044: // See if the current call frame is on the list of transparent
1045: // call frames.
1046:
1047: transparent = false;
1048:
1049: frame = Migrate.GetCallFrame(interp, 0);
1050: for (i = Util.GetStackSize(info.transparentFrames) - 1; i >= 0; i--) {
1051: transFrame = (CallFrame) Util.GetStackValue(
1052: info.transparentFrames, i);
1053:
1054: if (frame == transFrame) {
1055: transparent = true;
1056: break;
1057: }
1058: }
1059:
1060: // If this is a transparent call frame, return the namespace
1061: // context one level up.
1062:
1063: if (transparent) {
1064: frame = Migrate.GetCallFrame(interp, 1);
1065: if (frame != null) {
1066: contextNs = ItclAccess.getCallFrameNamespace(frame);
1067: } else {
1068: contextNs = Namespace.getGlobalNamespace(interp);
1069: }
1070: } else {
1071: contextNs = Namespace.getCurrentNamespace(interp);
1072: }
1073: return contextNs;
1074: }
1075:
1076: /*
1077: * ------------------------------------------------------------------------
1078: * Itcl_ParseNamespPath -> Util.ParseNamespPath
1079: *
1080: * Parses a reference to a namespace element of the form:
1081: *
1082: * namesp::namesp::namesp::element
1083: *
1084: * Returns and object that contains a head and tail part.
1085: * head part ("namesp::namesp::namesp"), tail part ("element")
1086: * If the head part is missing, a the head member will be null
1087: * and the rest of the string is returneed as the tail.
1088: * ------------------------------------------------------------------------
1089: */
1090:
1091: static ParseNamespPathResult ParseNamespPath(String name) // path name to class member
1092: {
1093: int i;
1094: String head, tail;
1095:
1096: // Copy the name into the buffer and parse it. Look
1097: // backward from the end of the string to the first '::'
1098: // scope qualifier.
1099:
1100: i = name.length();
1101:
1102: while (--i > 0) {
1103: if (name.charAt(i) == ':' && name.charAt(i - 1) == ':') {
1104: break;
1105: }
1106: }
1107:
1108: // Found head/tail parts. If there are extra :'s, keep backing
1109: // up until the head is found. This supports the Tcl namespace
1110: // behavior, which allows names like "foo:::bar".
1111:
1112: if (i > 0) {
1113: tail = name.substring(i + 1);
1114:
1115: while (i > 0 && name.charAt(i - 1) == ':') {
1116: i--;
1117: }
1118: head = name.substring(0, i);
1119: }
1120:
1121: // No :: separators--the whole name is treated as a tail.
1122:
1123: else {
1124: tail = name;
1125: head = null;
1126: }
1127:
1128: return new ParseNamespPathResult(head, tail);
1129: }
1130:
1131: static class ParseNamespPathResult {
1132: String head;
1133: String tail;
1134:
1135: ParseNamespPathResult(String head, String tail) {
1136: this .head = head;
1137: this .tail = tail;
1138: }
1139: }
1140:
1141: /*
1142: * ------------------------------------------------------------------------
1143: * Itcl_DecodeScopedCommand -> Util.DecodeScopedCommand
1144: *
1145: * Decodes a scoped command of the form:
1146: *
1147: * namespace inscope <namesp> <command>
1148: *
1149: * If the given string is not a scoped value, this procedure does
1150: * nothing and returns a null rNs and the passed in name
1151: * value as the rCmd value. If the string is a scoped value
1152: * then it is decoded, and the namespace, and the simple command
1153: * string are returned. If anything goes wrong, this procedure
1154: * raises a TclException.
1155: * ------------------------------------------------------------------------
1156: */
1157:
1158: static DecodeScopedCommandResult DecodeScopedCommand(Interp interp, // current interpreter
1159: String name) // string to be decoded
1160: throws TclException {
1161: Namespace ns = null;
1162: String cmdName;
1163: final int len = name.length();
1164: int pos;
1165: TclObject[] listv = null;
1166: TclException ex = null;
1167:
1168: cmdName = name;
1169:
1170: if ((len > 17) && name.startsWith("namespace ")) {
1171: for (pos = 9; (pos < len && name.charAt(pos) == ' '); pos++) {
1172: // empty body: skip over spaces
1173: }
1174: if (((pos + 8) <= len)
1175: && (name.charAt(pos) == 'i')
1176: && (name.substring(pos, pos + 8).equals("inscope "))) {
1177: try {
1178: listv = TclList.getElements(interp, TclString
1179: .newInstance(name));
1180: } catch (TclException e) {
1181: ex = e;
1182: }
1183: if (ex == null) {
1184: if (listv.length != 4) {
1185: // Create exception, then add error info below before throwing
1186: ex = new TclException(
1187: interp,
1188: "malformed command \""
1189: + name
1190: + "\": should be \""
1191: + "namespace inscope namesp command\"");
1192: } else {
1193: String findNS = listv[2].toString();
1194: ns = Namespace.findNamespace(interp, findNS,
1195: null, TCL.LEAVE_ERR_MSG);
1196:
1197: if (ns == null) {
1198: ex = new TclException(interp, interp
1199: .getResult().toString());
1200: } else {
1201: cmdName = listv[3].toString();
1202: }
1203: }
1204: }
1205:
1206: if (ex != null) {
1207: String msg = "\n (while decoding scoped command \""
1208: + name + "\")";
1209: interp.addErrorInfo(msg);
1210: throw ex;
1211: }
1212: }
1213: }
1214:
1215: DecodeScopedCommandResult r = new DecodeScopedCommandResult();
1216: r.rNS = ns;
1217: r.rCmd = cmdName;
1218: return r;
1219: }
1220:
1221: static class DecodeScopedCommandResult {
1222: Namespace rNS; // returns: namespace for scoped value
1223: String rCmd; // returns: simple command word
1224: }
1225:
1226: /*
1227: * ------------------------------------------------------------------------
1228: * Itcl_EvalArgs -> Util.EvalArgs
1229: *
1230: * This procedure invokes a list of (objc,objv) arguments as a
1231: * single command. It is similar to Tcl_EvalObj, but it doesn't
1232: * do any parsing or compilation. It simply treats the first
1233: * argument as a command and invokes that command in the current
1234: * context.
1235: *
1236: * Returns if successful. Otherwise, this procedure raises
1237: * a TclException.
1238: * ------------------------------------------------------------------------
1239: */
1240:
1241: static void EvalArgs(Interp interp, // current interpreter
1242: TclObject[] objv) // argument objects
1243: throws TclException {
1244: WrappedCommand wcmd;
1245: TclObject cmdline = null;
1246: TclObject[] cmdlinev;
1247:
1248: // Resolve command name to WrappedCommand
1249:
1250: wcmd = Namespace.findCommand(interp, objv[0].toString(), null,
1251: 0);
1252:
1253: cmdlinev = objv;
1254:
1255: // If the command is still not found, handle it with the
1256: // "unknown" proc.
1257:
1258: if (wcmd == null) {
1259: wcmd = Namespace.findCommand(interp, "unknown", null,
1260: TCL.GLOBAL_ONLY);
1261:
1262: if (wcmd == null) {
1263: interp.resetResult();
1264: throw new TclException(interp,
1265: "invalid command name \"" + objv[0].toString()
1266: + "\"");
1267: }
1268:
1269: cmdline = Util.CreateArgs(interp, "unknown", objv, 0);
1270: cmdlinev = TclList.getElements(interp, cmdline);
1271: }
1272:
1273: // Finally, invoke the command's cmdProc()
1274:
1275: interp.resetResult();
1276: wcmd.cmd.cmdProc(interp, cmdlinev);
1277: }
1278:
1279: /*
1280: * ------------------------------------------------------------------------
1281: * Itcl_CreateArgs -> Util.CreateArgs
1282: *
1283: * This procedure takes a string and a list of objv arguments,
1284: * and glues them together in a single list. This is useful when
1285: * a command word needs to be prepended or substituted into a command
1286: * line before it is executed. The arguments are returned in a single
1287: * list object, and they can be retrieved by calling
1288: * Tcl_ListObjGetElements. When the arguments are no longer needed,
1289: * they should be discarded by decrementing the reference count for
1290: * the list object.
1291: *
1292: * Returns a list object containing the arguments.
1293: * ------------------------------------------------------------------------
1294: */
1295:
1296: static TclObject CreateArgs(Interp interp, // current interpreter
1297: String string, // first command word
1298: TclObject[] objv, // argument objects, can be null
1299: int skip) // number of argument objects to skip
1300: throws TclException {
1301: int i;
1302: TclObject list;
1303:
1304: list = TclList.newInstance();
1305: if (string != null) {
1306: TclList.append(interp, list, TclString.newInstance(string));
1307: }
1308:
1309: if (objv != null) {
1310: for (i = skip; i < objv.length; i++) {
1311: TclList.append(interp, list, objv[i]);
1312: }
1313: }
1314:
1315: list.preserve();
1316: return list;
1317: }
1318:
1319: /*
1320: * ------------------------------------------------------------------------
1321: * Tcl_DStringStartSublist -> Util.StartSublist
1322: *
1323: * This procedure appends a open brace character to a StringBuffer.
1324: * ------------------------------------------------------------------------
1325: */
1326:
1327: static void StartSublist(StringBuffer buffer) {
1328: if (NeedSpace(buffer)) {
1329: buffer.append(" {");
1330: } else {
1331: buffer.append('{');
1332: }
1333: }
1334:
1335: /*
1336: * ------------------------------------------------------------------------
1337: * Tcl_DStringEndSublist -> Util.EndSublist
1338: *
1339: * This procedure appends a close brace character to a StringBuffer.
1340: * ------------------------------------------------------------------------
1341: */
1342: static void EndSublist(StringBuffer buffer) {
1343: buffer.append('}');
1344: }
1345:
1346: /*
1347: * ------------------------------------------------------------------------
1348: * Tcl_DStringAppendElement -> Util.AppendElement
1349: *
1350: * This procedure appends a list element to a StringBuffer.
1351: * ------------------------------------------------------------------------
1352: */
1353:
1354: static void AppendElement(StringBuffer buffer, String elem) {
1355: if (NeedSpace(buffer)) {
1356: buffer.append(' ');
1357: }
1358: buffer.append(elem);
1359: }
1360:
1361: /*
1362: * ------------------------------------------------------------------------
1363: * TclNeedSpace -> Util.NeedSpace
1364: *
1365: * This procedure checks to see whether it is appropriate to
1366: * add a space before appending a new list element to an
1367: * existing string.
1368: * ------------------------------------------------------------------------
1369: */
1370:
1371: static boolean NeedSpace(StringBuffer buffer) {
1372: final int len = buffer.length();
1373:
1374: // A space is needed unless either
1375: // (a) we're at the start of the string, or
1376:
1377: if (len == 0) {
1378: return false;
1379: }
1380:
1381: // (b) we're at the start of a nested list-element, quoted with an
1382: // open curly brace; we can be nested arbitrarily deep, so long
1383: // as the first curly brace starts an element, so backtrack over
1384: // open curly braces that are trailing characters of the string; and
1385:
1386: int end = len - 1;
1387: while (buffer.charAt(end) == '{') {
1388: if (end == 0) {
1389: return false;
1390: }
1391: end--;
1392: }
1393:
1394: // (c) the trailing character of the string is already a list-element
1395: // separator. With the condition that the penultimate character
1396: // is not a backslash.
1397:
1398: if (Character.isSpaceChar(buffer.charAt(end))
1399: && ((end == 0) || (buffer.charAt(end - 1) != '\\'))) {
1400: return false;
1401: }
1402:
1403: return true;
1404: }
1405:
1406: } // end class Util
|