0001: /*
0002: * Namespace.java
0003: *
0004: * Copyright (c) 1993-1997 Lucent Technologies.
0005: * Copyright (c) 1997 Sun Microsystems, Inc.
0006: * Copyright (c) 1998-1999 by Scriptics Corporation.
0007: * Copyright (c) 1999-2005 Moses DeJong
0008: *
0009: * Originally implemented by
0010: * Michael J. McLennan
0011: * Bell Labs Innovations for Lucent Technologies
0012:
0013: * mmclennan@lucent.com
0014: *
0015: * See the file "license.terms" for information on usage and
0016: * redistribution of this file, and for a DISCLAIMER OF ALL
0017: * WARRANTIES.
0018: *
0019: * RCS: @(#) $Id: Namespace.java,v 1.6 2006/01/26 19:49:18 mdejong Exp $
0020: */
0021:
0022: package tcl.lang;
0023:
0024: import java.util.*;
0025:
0026: // This structure contains a cached pointer to a namespace that is the
0027: // result of resolving the namespace's name in some other namespace. It is
0028: // the internal representation for a nsName object. It contains the
0029: // pointer along with some information that is used to check the cached
0030: // pointer's validity. (ported Tcl_Namespace to Namespace)
0031:
0032: public class Namespace {
0033: public String name; // The namespace's simple (unqualified)
0034: // name. This contains no ::'s. The name of
0035: // the global namespace is "" although "::"
0036: // is an synonym.
0037: public String fullName; // The namespace's fully qualified name.
0038: // This starts with ::.
0039: public DeleteProc deleteProc; // method to invoke when namespace is deleted
0040:
0041: public Namespace parent; // reference to the namespace that contains
0042: // this one. null is this is the global namespace.
0043: public HashMap childTable; // Contains any child namespaces. Indexed
0044: // by strings; values are references to
0045: // Namespace objects
0046: public long nsId; // Unique id for the namespace.
0047:
0048: public Interp interp; // The interpreter containing this namespace.
0049:
0050: public int flags; // OR-ed combination of the namespace
0051: // status flags NS_DYING and NS_DEAD (listed below)
0052:
0053: public int activationCount; // Number of "activations" or active call
0054: // frames for this namespace that are on
0055: // the Tcl call stack. The namespace won't
0056: // be freed until activationCount becomes zero.
0057:
0058: public int refCount; // Count of references by nsName
0059: // objects. The namespace can't be freed
0060: // until refCount becomes zero.
0061:
0062: public HashMap cmdTable; // Contains all the commands currently
0063: // registered in the namespace. Indexed by
0064: // strings; values have type (WrappedCommand).
0065: // Commands imported by Tcl_Import have
0066: // Command structures that point (via an
0067: // ImportedCmdRef structure) to the
0068: // Command structure in the source
0069: // namespace's command table.
0070: public HashMap varTable; // Contains all the (global) variables
0071: // currently in this namespace. Indexed
0072: // by strings; values have type (Var).
0073:
0074: public String[] exportArray; // Reference to an array of string patterns
0075: // specifying which commands are exported.
0076: // A pattern may include "string match"
0077: // style wildcard characters to specify
0078: // multiple commands; however, no namespace
0079: // qualifiers are allowed. null if no
0080: // export patterns are registered.
0081:
0082: public int numExportPatterns; // Number of export patterns currently
0083: // registered using "namespace export".
0084:
0085: public int maxExportPatterns; // Mumber of export patterns for which
0086: // space is currently allocated.
0087:
0088: public Resolver resolver;
0089:
0090: // If non-null, this object overrides the
0091: // usual command and variable resolution
0092: // mechanism in Tcl. This procedure is invoked
0093: // within findCommand and findNamespaceVar to
0094: // resolve all command and variable references
0095: // within the namespace.
0096:
0097: // When printing out a Namespace use the full namespace name string
0098:
0099: public String toString() {
0100: return fullName;
0101: }
0102:
0103: // This interface is used to provide a callback when a namespace is deleted
0104: // (ported Tcl_NamespaceDeleteProc to Namespace.DeleteProc)
0105:
0106: public static interface DeleteProc {
0107: public void delete();
0108: }
0109:
0110: // (ported ResolvedNsName to Namespace.ResolvedNsName)
0111:
0112: static class ResolvedNsName {
0113: Namespace ns; // reference to namespace object
0114: long nsId; // sPtr's unique namespace id. Used to
0115: // verify that ns is still valid
0116: // (e.g., it's possible that the namespace
0117: // was deleted and a new one created at
0118: // the same address).
0119:
0120: Namespace refNs; // reference to the namespace containing the
0121: // reference (not the namespace that
0122: // contains the referenced namespace).
0123: int refCount; // Reference count: 1 for each nsName
0124: // object that has a pointer to this
0125: // ResolvedNsName structure as its internal
0126: // rep. This structure can be freed when
0127: // refCount becomes zero.
0128: }
0129:
0130: // Flag passed to getNamespaceForQualName to indicate that it should
0131: // search for a namespace rather than a command or variable inside a
0132: // namespace. Note that this flag's value must not conflict with the values
0133: // of TCL.GLOBAL_ONLY, TCL.NAMESPACE_ONLY, or CREATE_NS_IF_UNKNOWN.
0134:
0135: public static final int FIND_ONLY_NS = 0x1000;
0136:
0137: // Initial size of array of namespace refs - used in resetShadowedCmdRefs()
0138:
0139: private static final int NUM_TRAIL_ELEMS = 5;
0140:
0141: // Count of the number of namespaces created. This value is used as a
0142: // unique id for each namespace.
0143:
0144: private static long numNsCreated = 0;
0145: private static Object nsMutex = new Object();
0146:
0147: //
0148: // Flags used to represent the status of a namespace:
0149: //
0150: // NS_DYING - 1 means deleteNamespace has been called to delete the
0151: // namespace but there are still active call frames on the Tcl
0152: // stack that refer to the namespace. When the last call frame
0153: // referring to it has been popped, it's variables and command
0154: // will be destroyed and it will be marked "dead" (NS_DEAD).
0155: // The namespace can no longer be looked up by name.
0156: // NS_DEAD - 1 means deleteNamespace has been called to delete the
0157: // namespace and no call frames still refer to it. Its
0158: // variables and command have already been destroyed. This bit
0159: // allows the namespace resolution code to recognize that the
0160: // namespace is "deleted". When the last namespaceName object
0161: // in any byte code code unit that refers to the namespace has
0162: // been freed (i.e., when the namespace's refCount is 0), the
0163: // namespace's storage will be freed.
0164:
0165: static final int NS_DYING = 0x01;
0166: static final int NS_DEAD = 0x02;
0167:
0168: // Flag passed to getNamespaceForQualName to have it create all namespace
0169: // components of a namespace-qualified name that cannot be found. The new
0170: // namespaces are created within their specified parent. Note that this
0171: // flag's value must not conflict with the values of the flags
0172: // TCL.GLOBAL_ONLY, TCL.NAMESPACE_ONLY, and FIND_ONLY_NS
0173:
0174: public static final int CREATE_NS_IF_UNKNOWN = 0x800;
0175:
0176: /*
0177: *----------------------------------------------------------------------
0178: *
0179: * Tcl_GetCurrentNamespace -> getCurrentNamespace
0180: *
0181: * Returns a reference to an interpreter's currently active namespace.
0182: *
0183: * Results:
0184: * Returns a reference to the interpreter's current namespace.
0185: *
0186: * Side effects:
0187: * None.
0188: *
0189: *----------------------------------------------------------------------
0190: */
0191:
0192: public static Namespace getCurrentNamespace(Interp interp) {
0193: if (interp.varFrame != null) {
0194: return interp.varFrame.ns;
0195: } else {
0196: return interp.globalNs;
0197: }
0198: }
0199:
0200: /*
0201: *----------------------------------------------------------------------
0202: *
0203: * Tcl_GetGlobalNamespace -> getGlobalNamespace
0204: *
0205: * Returns a reference to an interpreter's global :: namespace.
0206: *
0207: * Results:
0208: * Returns a reference to the specified interpreter's global namespace.
0209: *
0210: * Side effects:
0211: * None.
0212: *
0213: *----------------------------------------------------------------------
0214: */
0215:
0216: public static Namespace getGlobalNamespace(Interp interp) {
0217: return interp.globalNs;
0218: }
0219:
0220: /*
0221: *----------------------------------------------------------------------
0222: *
0223: * Tcl_PushCallFrame -> pushCallFrame
0224: *
0225: * Pushes a new call frame onto the interpreter's Tcl call stack.
0226: * Called when executing a Tcl procedure or a "namespace eval" or
0227: * "namespace inscope" command.
0228: *
0229: * Results:
0230: * Returns if successful, raises TclException if something goes wrong.
0231: *
0232: * Side effects:
0233: * Modifies the interpreter's Tcl call stack.
0234: *
0235: *----------------------------------------------------------------------
0236: */
0237:
0238: public static void pushCallFrame(Interp interp, // Interpreter in which the new call frame
0239: // is to be pushed.
0240: CallFrame frame, // Points to a call frame object to
0241: // push. The call frame will be initialized
0242: // by this method. The caller can pop the frame
0243: // later with popCallFrame.
0244: Namespace namespace, // Points to the namespace in which the
0245: // interpreter's current namespace will
0246: // be used.
0247: boolean isProcCallFrame) // If true, the frame represents a
0248: // called Tcl procedure and may have local
0249: // vars. Vars will ordinarily be looked up
0250: // in the frame. If new variables are
0251: // created, they will be created in the
0252: // frame. If false, the frame is for a
0253: // "namespace eval" or "namespace inscope"
0254: // command and var references are treated
0255: // as references to namespace variables.
0256: {
0257: Namespace ns;
0258:
0259: if (namespace == null) {
0260: ns = getCurrentNamespace(interp);
0261: } else {
0262: ns = namespace;
0263: if ((ns.flags & NS_DEAD) != 0) {
0264: throw new TclRuntimeError(
0265: "Trying to push call frame for dead namespace");
0266: }
0267: }
0268:
0269: ns.activationCount++;
0270: frame.ns = ns;
0271: frame.isProcCallFrame = isProcCallFrame;
0272: frame.objv = null;
0273:
0274: frame.caller = interp.frame;
0275: frame.callerVar = interp.varFrame;
0276:
0277: if (interp.varFrame != null) {
0278: frame.level = (interp.varFrame.level + 1);
0279: } else {
0280: frame.level = 1;
0281: }
0282:
0283: // FIXME : does Jacl need a procPtr in the CallFrame class?
0284: //frame.procPtr = null; // no called procedure
0285:
0286: frame.varTable = null; // and no local variables
0287:
0288: // Compiled locals are not part of Jacl's CallFrame
0289:
0290: // Push the new call frame onto the interpreter's stack of procedure
0291: // call frames making it the current frame.
0292:
0293: interp.frame = frame;
0294: interp.varFrame = frame;
0295: }
0296:
0297: /*
0298: *----------------------------------------------------------------------
0299: *
0300: * Tcl_PopCallFrame -> popCallFrame
0301: *
0302: * Removes a call frame from the Tcl call stack for the interpreter.
0303: * Called to remove a frame previously pushed by Tcl_PushCallFrame.
0304: *
0305: * Results:
0306: * None.
0307: *
0308: * Side effects:
0309: * Modifies the call stack of the interpreter. Resets various fields of
0310: * the popped call frame. If a namespace has been deleted and
0311: * has no more activations on the call stack, the namespace is
0312: * destroyed.
0313: *
0314: *----------------------------------------------------------------------
0315: */
0316:
0317: public static void popCallFrame(Interp interp) {
0318: CallFrame frame = interp.frame;
0319: int saveErrFlag;
0320: Namespace ns;
0321:
0322: // It's important to remove the call frame from the interpreter's stack
0323: // of call frames before deleting local variables, so that traces
0324: // invoked by the variable deletion don't see the partially-deleted
0325: // frame.
0326:
0327: interp.frame = frame.caller;
0328: interp.varFrame = frame.callerVar;
0329:
0330: // Delete the local variables. As a hack, we save then restore the
0331: // ERR_IN_PROGRESS flag in the interpreter. The problem is that there
0332: // could be unset traces on the variables, which cause scripts to be
0333: // evaluated. This will clear the ERR_IN_PROGRESS flag, losing stack
0334: // trace information if the procedure was exiting with an error. The
0335: // code below preserves the flag. Unfortunately, that isn't really
0336: // enough: we really should preserve the errorInfo variable too
0337: // (otherwise a nested error in the trace script will trash errorInfo).
0338: // What's really needed is a general-purpose mechanism for saving and
0339: // restoring interpreter state.
0340:
0341: saveErrFlag = (interp.flags & Parser.ERR_IN_PROGRESS);
0342:
0343: if (frame.varTable != null) {
0344: Var.deleteVars(interp, frame.varTable);
0345: frame.varTable = null;
0346: }
0347:
0348: interp.flags |= saveErrFlag;
0349:
0350: // Decrement the namespace's count of active call frames. If the
0351: // namespace is "dying" and there are no more active call frames,
0352: // call Tcl_DeleteNamespace to destroy it.
0353:
0354: ns = frame.ns;
0355: ns.activationCount--;
0356: if (((ns.flags & NS_DYING) != 0) && (ns.activationCount == 0)) {
0357: deleteNamespace(ns);
0358: }
0359: frame.ns = null;
0360: }
0361:
0362: /*
0363: *----------------------------------------------------------------------
0364: *
0365: * Tcl_CreateNamespace --
0366: *
0367: * Creates a new namespace with the given name. If there is no
0368: * active namespace (i.e., the interpreter is being initialized),
0369: * the global :: namespace is created and returned.
0370: *
0371: * Results:
0372: * Returns a reference to the new namespace if successful. If the
0373: * namespace already exists or if another error occurs, this routine
0374: * returns null, along with an error message in the interpreter's
0375: * result object.
0376: *
0377: * Side effects:
0378: * If the name contains "::" qualifiers and a parent namespace does
0379: * not already exist, it is automatically created.
0380: *
0381: *----------------------------------------------------------------------
0382: */
0383:
0384: public static Namespace createNamespace(Interp interp, // Interpreter in which a new namespace
0385: // is being created
0386: String name, // Name for the new namespace. May be a
0387: // qualified name with names of ancestor
0388: // namespaces separated by "::"s.
0389: DeleteProc deleteProc // Procedure called when namespace is deleted.
0390: // null if no procedure should be called
0391: ) {
0392: Namespace ns, ancestor;
0393: Namespace parent;
0394: Namespace globalNs = getGlobalNamespace(interp);
0395: String simpleName;
0396: StringBuffer buffer1, buffer2;
0397:
0398: // If there is no active namespace, the interpreter is being
0399: // initialized.
0400:
0401: if ((globalNs == null) && (interp.varFrame == null)) {
0402: // Treat this namespace as the global namespace, and avoid
0403: // looking for a parent.
0404:
0405: parent = null;
0406: simpleName = "";
0407: } else if (name.length() == 0) {
0408: /*
0409: TclObject tobj = interp.getResult();
0410: // FIXME : is there a test case to check this error result?
0411: TclString.append(tobj,
0412: "can't create namespace \"\": only global namespace can have empty name");
0413: */
0414:
0415: // FIXME : is there a test case to check this error result?
0416: interp
0417: .setResult("can't create namespace \"\": only global namespace can have empty name");
0418: return null;
0419: } else {
0420: // Find the parent for the new namespace.
0421:
0422: GetNamespaceForQualNameResult gnfqnr = interp.getnfqnResult;
0423: getNamespaceForQualName(interp, name, null,
0424: (CREATE_NS_IF_UNKNOWN | TCL.LEAVE_ERR_MSG), gnfqnr);
0425: parent = gnfqnr.ns;
0426: simpleName = gnfqnr.simpleName;
0427:
0428: // If the unqualified name at the end is empty, there were trailing
0429: // "::"s after the namespace's name which we ignore. The new
0430: // namespace was already (recursively) created and is referenced
0431: // by parent.
0432:
0433: if (simpleName.length() == 0) {
0434: return parent;
0435: }
0436:
0437: // Check for a bad namespace name and make sure that the name
0438: // does not already exist in the parent namespace.
0439:
0440: if (parent.childTable.get(simpleName) != null) {
0441: /*
0442: TclObject tobj = interp.getResult();
0443: // FIXME : is there a test case to check this error result?
0444: TclString.append(tobj,
0445: "can't create namespace \"" + name + "\": already exists");
0446: */
0447:
0448: // FIXME : is there a test case to check this error result?
0449: interp.setResult("can't create namespace \"" + name
0450: + "\": already exists");
0451: return null;
0452: }
0453: }
0454:
0455: // Create the new namespace and root it in its parent. Increment the
0456: // count of namespaces created.
0457:
0458: ns = new Namespace();
0459: ns.name = simpleName;
0460: ns.fullName = null; // set below
0461: //ns.clientData = clientData;
0462: ns.deleteProc = deleteProc;
0463: ns.parent = parent;
0464: ns.childTable = new HashMap();
0465: synchronized (nsMutex) {
0466: numNsCreated++;
0467: ns.nsId = numNsCreated;
0468: }
0469: ns.interp = interp;
0470: ns.flags = 0;
0471: ns.activationCount = 0;
0472: // FIXME : there was a problem with the refcount because
0473: // when the namespace was deleted the refocount was 0.
0474: // We avoid this by just using a refcount of 1 for now.
0475: // We can do ignore the refCount because GC will reclaim mem.
0476: //ns.refCount = 0;
0477: ns.refCount = 1;
0478: ns.cmdTable = new HashMap();
0479: ns.varTable = new HashMap();
0480: ns.exportArray = null;
0481: ns.numExportPatterns = 0;
0482: ns.maxExportPatterns = 0;
0483:
0484: // Jacl does not use these tcl compiler specific members
0485: //ns.cmdRefEpoch = 0;
0486: //ns.resolverEpoch = 0;
0487:
0488: ns.resolver = null;
0489:
0490: if (parent != null) {
0491: parent.childTable.put(simpleName, ns);
0492: }
0493:
0494: // Build the fully qualified name for this namespace.
0495:
0496: buffer1 = new StringBuffer();
0497: buffer2 = new StringBuffer();
0498: for (ancestor = ns; ancestor != null; ancestor = ancestor.parent) {
0499: if (ancestor != globalNs) {
0500: buffer1.append("::");
0501: buffer1.append(ancestor.name);
0502: }
0503: buffer1.append(buffer2);
0504:
0505: buffer2.setLength(0);
0506: buffer2.append(buffer1);
0507: buffer1.setLength(0);
0508: }
0509:
0510: name = buffer2.toString();
0511: ns.fullName = name;
0512:
0513: // Return a reference to the new namespace.
0514:
0515: return ns;
0516: }
0517:
0518: /*
0519: *----------------------------------------------------------------------
0520: *
0521: * Tcl_DeleteNamespace -> deleteNamespace
0522: *
0523: * Deletes a namespace and all of the commands, variables, and other
0524: * namespaces within it.
0525: *
0526: * Results:
0527: * None.
0528: *
0529: * Side effects:
0530: * When a namespace is deleted, it is automatically removed as a
0531: * child of its parent namespace. Also, all its commands, variables
0532: * and child namespaces are deleted.
0533: *
0534: *----------------------------------------------------------------------
0535: */
0536:
0537: public static void deleteNamespace(Namespace namespace) {
0538: Namespace ns = namespace;
0539: Interp interp = ns.interp;
0540: Namespace globalNs = getGlobalNamespace(interp);
0541:
0542: // If the namespace is on the call frame stack, it is marked as "dying"
0543: // (NS_DYING is OR'd into its flags): the namespace can't be looked up
0544: // by name but its commands and variables are still usable by those
0545: // active call frames. When all active call frames referring to the
0546: // namespace have been popped from the Tcl stack, popCallFrame will
0547: // call this procedure again to delete everything in the namespace.
0548: // If no nsName objects refer to the namespace (i.e., if its refCount
0549: // is zero), its commands and variables are deleted and the storage for
0550: // its namespace structure is freed. Otherwise, if its refCount is
0551: // nonzero, the namespace's commands and variables are deleted but the
0552: // structure isn't freed. Instead, NS_DEAD is OR'd into the structure's
0553: // flags to allow the namespace resolution code to recognize that the
0554: // namespace is "deleted".
0555:
0556: if (ns.activationCount > 0) {
0557: ns.flags |= NS_DYING;
0558: if (ns.parent != null) {
0559: ns.parent.childTable.remove(ns.name);
0560: }
0561: ns.parent = null;
0562: } else {
0563: // Delete the namespace and everything in it. If this is the global
0564: // namespace, then clear it but don't free its storage unless the
0565: // interpreter is being torn down.
0566:
0567: teardownNamespace(ns);
0568:
0569: if ((ns != globalNs)
0570: || ((interp.flags & Parser.DELETED) != 0)) {
0571: // If this is the global namespace, then it may have residual
0572: // "errorInfo" and "errorCode" variables for errors that
0573: // occurred while it was being torn down. Try to clear the
0574: // variable list one last time.
0575:
0576: Var.deleteVars(ns.interp, ns.varTable);
0577:
0578: ns.childTable.clear();
0579: ns.cmdTable.clear();
0580:
0581: // If the reference count is 0, then discard the namespace.
0582: // Otherwise, mark it as "dead" so that it can't be used.
0583:
0584: if (ns.refCount == 0) {
0585: free(ns);
0586: } else {
0587: ns.flags |= NS_DEAD;
0588: }
0589: }
0590: }
0591: }
0592:
0593: /*
0594: *----------------------------------------------------------------------
0595: *
0596: * TclTeardownNamespace -> teardownNamespace
0597: *
0598: * Used internally to dismantle and unlink a namespace when it is
0599: * deleted. Divorces the namespace from its parent, and deletes all
0600: * commands, variables, and child namespaces.
0601: *
0602: * This is kept separate from Tcl_DeleteNamespace so that the global
0603: * namespace can be handled specially. Global variables like
0604: * "errorInfo" and "errorCode" need to remain intact while other
0605: * namespaces and commands are torn down, in case any errors occur.
0606: *
0607: * Results:
0608: * None.
0609: *
0610: * Side effects:
0611: * Removes this namespace from its parent's child namespace hashtable.
0612: * Deletes all commands, variables and namespaces in this namespace.
0613: * If this is the global namespace, the "errorInfo" and "errorCode"
0614: * variables are left alone and deleted later.
0615: *
0616: *----------------------------------------------------------------------
0617: */
0618:
0619: static void teardownNamespace(Namespace ns) {
0620: Interp interp = ns.interp;
0621: Namespace childNs;
0622: WrappedCommand cmd;
0623: Namespace globalNs = getGlobalNamespace(interp);
0624: int i;
0625:
0626: // Start by destroying the namespace's variable table,
0627: // since variables might trigger traces.
0628:
0629: if (ns == globalNs) {
0630: // This is the global namespace, so be careful to preserve the
0631: // "errorInfo" and "errorCode" variables. These might be needed
0632: // later on if errors occur while deleting commands. We are careful
0633: // to destroy and recreate the "errorInfo" and "errorCode"
0634: // variables, in case they had any traces on them.
0635:
0636: String errorInfoStr, errorCodeStr;
0637:
0638: try {
0639: errorInfoStr = interp.getVar("errorInfo",
0640: TCL.GLOBAL_ONLY).toString();
0641: } catch (TclException e) {
0642: errorInfoStr = null;
0643: }
0644:
0645: try {
0646: errorCodeStr = interp.getVar("errorCode",
0647: TCL.GLOBAL_ONLY).toString();
0648: } catch (TclException e) {
0649: errorCodeStr = null;
0650: }
0651:
0652: Var.deleteVars(interp, ns.varTable);
0653:
0654: if (errorInfoStr != null) {
0655: try {
0656: interp.setVar("errorInfo", errorInfoStr,
0657: TCL.GLOBAL_ONLY);
0658: } catch (TclException e) {
0659: // ignore an exception while setting this var
0660: }
0661: }
0662: if (errorCodeStr != null) {
0663: try {
0664: interp.setVar("errorCode", errorCodeStr,
0665: TCL.GLOBAL_ONLY);
0666: } catch (TclException e) {
0667: // ignore an exception while setting this var
0668: }
0669: }
0670: } else {
0671: // Variable table should be cleared.
0672: Var.deleteVars(interp, ns.varTable);
0673: }
0674:
0675: // Remove the namespace from its parent's child hashtable.
0676:
0677: if (ns.parent != null) {
0678: ns.parent.childTable.remove(ns.name);
0679: }
0680: ns.parent = null;
0681:
0682: // Delete all the child namespaces.
0683: //
0684: // BE CAREFUL: When each child is deleted, it will divorce
0685: // itself from its parent. You can't traverse a hash table
0686: // properly if its elements are being deleted. We use only
0687: // the Tcl_FirstHashEntry function to be safe.
0688:
0689: while ((childNs = (Namespace) FirstHashEntry(ns.childTable)) != null) {
0690: deleteNamespace(childNs);
0691: }
0692:
0693: // Delete all commands in this namespace. Be careful when traversing the
0694: // hash table: when each command is deleted, it removes itself from the
0695: // command table.
0696:
0697: while ((cmd = (WrappedCommand) FirstHashEntry(ns.cmdTable)) != null) {
0698: interp.deleteCommandFromToken(cmd);
0699: }
0700:
0701: // Free the namespace's export pattern array.
0702:
0703: if (ns.exportArray != null) {
0704: ns.exportArray = null;
0705: ns.numExportPatterns = 0;
0706: ns.maxExportPatterns = 0;
0707: }
0708:
0709: // Callback invoked when namespace is deleted
0710:
0711: if (ns.deleteProc != null) {
0712: ns.deleteProc.delete();
0713: }
0714: ns.deleteProc = null;
0715:
0716: // Reset the namespace's id field to ensure that this namespace won't
0717: // be interpreted as valid by, e.g., the cache validation code for
0718: // cached command references in Tcl_GetCommandFromObj.
0719:
0720: ns.nsId = 0;
0721: }
0722:
0723: /*
0724: *----------------------------------------------------------------------
0725: *
0726: * NamespaceFree -> free
0727: *
0728: * Called after a namespace has been deleted, when its
0729: * reference count reaches 0. Frees the data structure
0730: * representing the namespace.
0731: *
0732: * Results:
0733: * None.
0734: *
0735: * Side effects:
0736: * None.
0737: *
0738: *----------------------------------------------------------------------
0739: */
0740:
0741: static void free(Namespace ns) {
0742: // Most of the namespace's contents are freed when the namespace is
0743: // deleted by Tcl_DeleteNamespace. All that remains is to free its names
0744: // (for error messages), and the structure itself.
0745:
0746: ns.name = null;
0747: ns.fullName = null;
0748: }
0749:
0750: /*
0751: *----------------------------------------------------------------------
0752: *
0753: * Tcl_Export -> exportList
0754: *
0755: * Makes all the commands matching a pattern available to later be
0756: * imported from the namespace specified by namespace (or the
0757: * current namespace if namespace is null). The specified pattern is
0758: * appended onto the namespace's export pattern list, which is
0759: * optionally cleared beforehand.
0760: *
0761: * Results:
0762: * Returns if successful, raises TclException if something goes wrong.
0763: *
0764: * Side effects:
0765: * Appends the export pattern onto the namespace's export list.
0766: * Optionally reset the namespace's export pattern list.
0767: *
0768: *----------------------------------------------------------------------
0769: */
0770:
0771: public static void exportList(Interp interp, // current interpreter
0772: Namespace namespace, // Points to the namespace from which
0773: // commands are to be exported. null for
0774: // the current namespace.
0775: String pattern, // String pattern indicating which commands
0776: // to export. This pattern may not include
0777: // any namespace qualifiers; only commands
0778: // in the specified namespace may be
0779: // exported.
0780: boolean resetListFirst // If true, resets the namespace's
0781: // export list before appending
0782: // If false, return an error if an imported
0783: // cmd conflicts
0784: ) throws TclException {
0785: final int INIT_EXPORT_PATTERNS = 5;
0786: Namespace ns, exportNs;
0787: Namespace currNs = getCurrentNamespace(interp);
0788: String simplePattern, patternCpy;
0789: int neededElems, len, i;
0790:
0791: // If the specified namespace is null, use the current namespace.
0792:
0793: if (namespace == null) {
0794: ns = currNs;
0795: } else {
0796: ns = namespace;
0797: }
0798:
0799: // If resetListFirst is true (nonzero), clear the namespace's export
0800: // pattern list.
0801:
0802: if (resetListFirst) {
0803: if (ns.exportArray != null) {
0804: for (i = 0; i < ns.numExportPatterns; i++) {
0805: ns.exportArray[i] = null;
0806: }
0807: ns.exportArray = null;
0808: ns.numExportPatterns = 0;
0809: ns.maxExportPatterns = 0;
0810: }
0811: }
0812:
0813: // Check that the pattern doesn't have namespace qualifiers.
0814:
0815: GetNamespaceForQualNameResult gnfqnr = interp.getnfqnResult;
0816: getNamespaceForQualName(interp, pattern, ns, TCL.LEAVE_ERR_MSG,
0817: gnfqnr);
0818: exportNs = gnfqnr.ns;
0819: simplePattern = gnfqnr.simpleName;
0820:
0821: if ((exportNs != ns) || (pattern.compareTo(simplePattern) != 0)) {
0822: throw new TclException(interp, "invalid export pattern \""
0823: + pattern + "\": pattern can't specify a namespace");
0824: }
0825:
0826: // Make sure there is room in the namespace's pattern array for the
0827: // new pattern.
0828:
0829: neededElems = ns.numExportPatterns + 1;
0830: if (ns.exportArray == null) {
0831: ns.exportArray = new String[INIT_EXPORT_PATTERNS];
0832: ns.numExportPatterns = 0;
0833: ns.maxExportPatterns = INIT_EXPORT_PATTERNS;
0834: } else if (neededElems > ns.maxExportPatterns) {
0835: int numNewElems = 2 * ns.maxExportPatterns;
0836: String[] newArray = new String[numNewElems];
0837: System.arraycopy(ns.exportArray, 0, newArray, 0,
0838: ns.numExportPatterns);
0839: ns.exportArray = newArray;
0840: ns.maxExportPatterns = numNewElems;
0841: }
0842:
0843: // Add the pattern to the namespace's array of export patterns.
0844:
0845: ns.exportArray[ns.numExportPatterns] = pattern;
0846: ns.numExportPatterns++;
0847: return;
0848: }
0849:
0850: /*
0851: *----------------------------------------------------------------------
0852: *
0853: * Tcl_AppendExportList -> appendExportList
0854: *
0855: * Appends onto the argument object the list of export patterns for the
0856: * specified namespace.
0857: *
0858: * Results:
0859: * The method will return when successful; in this case the object
0860: * referenced by obj has each export pattern appended to it. If an
0861: * error occurs, an exception and the interpreter's result
0862: * holds an error message.
0863: *
0864: * Side effects:
0865: * If necessary, the object referenced by obj is converted into
0866: * a list object.
0867: *
0868: *----------------------------------------------------------------------
0869: */
0870:
0871: static void appendExportList(Interp interp, // Interpreter used for error reporting.
0872: Namespace namespace, // Points to the namespace whose export
0873: // pattern list is appended onto obj.
0874: // null for the current namespace.
0875: TclObject obj // Points to the Tcl object onto which the
0876: // export pattern list is appended.
0877: ) throws TclException {
0878: Namespace ns;
0879: int i;
0880:
0881: // If the specified namespace is null, use the current namespace.
0882:
0883: if (namespace == null) {
0884: ns = getCurrentNamespace(interp);
0885: } else {
0886: ns = namespace;
0887: }
0888:
0889: // Append the export pattern list onto objPtr.
0890:
0891: for (i = 0; i < ns.numExportPatterns; i++) {
0892: TclList.append(interp, obj, TclString
0893: .newInstance(ns.exportArray[i]));
0894: }
0895: return;
0896: }
0897:
0898: /*
0899: *----------------------------------------------------------------------
0900: *
0901: * Tcl_Import -> importList
0902: *
0903: * Imports all of the commands matching a pattern into the namespace
0904: * specified by namespace (or the current namespace if namespace
0905: * is null). This is done by creating a new command (the "imported
0906: * command") that points to the real command in its original namespace.
0907: *
0908: * If matching commands are on the autoload path but haven't been
0909: * loaded yet, this command forces them to be loaded, then creates
0910: * the links to them.
0911: *
0912: * Results:
0913: * Returns if successful, raises TclException if something goes wrong.
0914: *
0915: * Side effects:
0916: * Creates new commands in the importing namespace. These indirect
0917: * calls back to the real command and are deleted if the real commands
0918: * are deleted.
0919: *
0920: *----------------------------------------------------------------------
0921: */
0922:
0923: public static void importList(Interp interp, // Current interpreter.
0924: Namespace namespace, // reference to the namespace into which the
0925: // commands are to be imported. null for
0926: // the current namespace.
0927: String pattern, // String pattern indicating which commands
0928: // to import. This pattern should be
0929: // qualified by the name of the namespace
0930: // from which to import the command(s).
0931: boolean allowOverwrite // If true, allow existing commands to
0932: // be overwritten by imported commands.
0933: // If 0, return an error if an imported
0934: // cmd conflicts with an existing one.
0935: ) throws TclException {
0936: Namespace ns, importNs;
0937: Namespace currNs = getCurrentNamespace(interp);
0938: String simplePattern, cmdName;
0939: WrappedCommand cmd, realCmd;
0940: ImportRef ref;
0941: WrappedCommand autoCmd, importedCmd;
0942: ImportedCmdData data;
0943: boolean wasExported;
0944: int i, result;
0945:
0946: // If the specified namespace is null, use the current namespace.
0947:
0948: if (namespace == null) {
0949: ns = currNs;
0950: } else {
0951: ns = namespace;
0952: }
0953:
0954: // First, invoke the "auto_import" command with the pattern
0955: // being imported. This command is part of the Tcl library.
0956: // It looks for imported commands in autoloaded libraries and
0957: // loads them in. That way, they will be found when we try
0958: // to create links below.
0959:
0960: autoCmd = findCommand(interp, "auto_import", null,
0961: TCL.GLOBAL_ONLY);
0962:
0963: if (autoCmd != null) {
0964: TclObject[] objv = new TclObject[2];
0965:
0966: objv[0] = TclString.newInstance("auto_import");
0967: objv[0].preserve();
0968: objv[1] = TclString.newInstance(pattern);
0969: objv[1].preserve();
0970:
0971: cmd = autoCmd;
0972: try {
0973: // Invoke the command with the arguments
0974: cmd.cmd.cmdProc(interp, objv);
0975: } finally {
0976: objv[0].release();
0977: objv[1].release();
0978: }
0979:
0980: interp.resetResult();
0981: }
0982:
0983: // From the pattern, find the namespace from which we are importing
0984: // and get the simple pattern (no namespace qualifiers or ::'s) at
0985: // the end.
0986:
0987: if (pattern.length() == 0) {
0988: throw new TclException(interp, "empty import pattern");
0989: }
0990:
0991: GetNamespaceForQualNameResult gnfqnr = interp.getnfqnResult;
0992: getNamespaceForQualName(interp, pattern, ns, TCL.LEAVE_ERR_MSG,
0993: gnfqnr);
0994: importNs = gnfqnr.ns;
0995: simplePattern = gnfqnr.simpleName;
0996:
0997: if (importNs == null) {
0998: throw new TclException(interp,
0999: "unknown namespace in import pattern \"" + pattern
1000: + "\"");
1001: }
1002: if (importNs == ns) {
1003: if (pattern == simplePattern) {
1004: throw new TclException(interp,
1005: "no namespace specified in import pattern \""
1006: + pattern + "\"");
1007: } else {
1008: throw new TclException(interp, "import pattern \""
1009: + pattern
1010: + "\" tries to import from namespace \""
1011: + importNs.name + "\" into itself");
1012: }
1013: }
1014:
1015: // Scan through the command table in the source namespace and look for
1016: // exported commands that match the string pattern. Create an "imported
1017: // command" in the current namespace for each imported command; these
1018: // commands redirect their invocations to the "real" command.
1019:
1020: for (Iterator iter = importNs.cmdTable.entrySet().iterator(); iter
1021: .hasNext();) {
1022: Map.Entry entry = (Map.Entry) iter.next();
1023: cmdName = (String) entry.getKey();
1024:
1025: if (Util.stringMatch(cmdName, simplePattern)) {
1026: // The command cmdName in the source namespace matches the
1027: // pattern. Check whether it was exported. If it wasn't,
1028: // we ignore it.
1029:
1030: wasExported = false;
1031: for (i = 0; i < importNs.numExportPatterns; i++) {
1032: if (Util.stringMatch(cmdName,
1033: importNs.exportArray[i])) {
1034: wasExported = true;
1035: break;
1036: }
1037: }
1038: if (!wasExported) {
1039: continue;
1040: }
1041:
1042: // Unless there is a name clash, create an imported command
1043: // in the current namespace that refers to cmdPtr.
1044:
1045: if ((ns.cmdTable.get(cmdName) == null)
1046: || allowOverwrite) {
1047: // Create the imported command and its client data.
1048: // To create the new command in the current namespace,
1049: // generate a fully qualified name for it.
1050:
1051: StringBuffer ds;
1052:
1053: ds = new StringBuffer();
1054: ds.append(ns.fullName);
1055: if (ns != interp.globalNs) {
1056: ds.append("::");
1057: }
1058: ds.append(cmdName);
1059:
1060: // Check whether creating the new imported command in the
1061: // current namespace would create a cycle of imported->real
1062: // command references that also would destroy an existing
1063: // "real" command already in the current namespace.
1064:
1065: cmd = (WrappedCommand) importNs.cmdTable
1066: .get(cmdName);
1067:
1068: if (cmd.cmd instanceof ImportedCmdData) {
1069: // This is actually an imported command, find
1070: // the real command it references
1071: realCmd = getOriginalCommand(cmd);
1072: if ((realCmd != null)
1073: && (realCmd.ns == currNs)
1074: && (currNs.cmdTable.get(cmdName) != null)) {
1075: throw new TclException(
1076: interp,
1077: "import pattern \""
1078: + pattern
1079: + "\" would create a loop containing command \""
1080: + ds.toString() + "\"");
1081: }
1082: }
1083:
1084: data = new ImportedCmdData();
1085:
1086: // Create the imported command inside the interp
1087: interp.createCommand(ds.toString(), data);
1088:
1089: // Lookup in the namespace for the new WrappedCommand
1090: importedCmd = findCommand(interp, ds.toString(),
1091: ns,
1092: (TCL.NAMESPACE_ONLY | TCL.LEAVE_ERR_MSG));
1093:
1094: data.realCmd = cmd;
1095: data.self = importedCmd;
1096:
1097: // Create an ImportRef structure describing this new import
1098: // command and add it to the import ref list in the "real"
1099: // command.
1100:
1101: ref = new ImportRef();
1102: ref.importedCmd = importedCmd;
1103: ref.next = cmd.importRef;
1104: cmd.importRef = ref;
1105: } else {
1106: throw new TclException(interp,
1107: "can't import command \"" + cmdName
1108: + "\": already exists");
1109: }
1110: }
1111: }
1112: return;
1113: }
1114:
1115: /*
1116: *----------------------------------------------------------------------
1117: *
1118: * Tcl_ForgetImport -> forgetImport
1119: *
1120: * Deletes previously imported commands. Given a pattern that may
1121: * include the name of an exporting namespace, this procedure first
1122: * finds all matching exported commands. It then looks in the namespace
1123: * specified by namespace for any corresponding previously imported
1124: * commands, which it deletes. If namespace is null, commands are
1125: * deleted from the current namespace.
1126: *
1127: * Results:
1128: * Returns if successful, raises TclException if something goes wrong.
1129: *
1130: * Side effects:
1131: * May delete commands.
1132: *
1133: *----------------------------------------------------------------------
1134: */
1135:
1136: static void forgetImport(Interp interp, // Current interpreter.
1137: Namespace namespace, // Points to the namespace from which
1138: // previously imported commands should be
1139: // removed. null for current namespace.
1140: String pattern // String pattern indicating which imported
1141: // commands to remove. This pattern should
1142: // be qualified by the name of the
1143: // namespace from which the command(s) were
1144: // imported.
1145: ) throws TclException {
1146: Namespace ns, importNs, actualCtx;
1147: String simplePattern, cmdName;
1148: WrappedCommand cmd;
1149:
1150: // If the specified namespace is null, use the current namespace.
1151:
1152: if (namespace == null) {
1153: ns = getCurrentNamespace(interp);
1154: } else {
1155: ns = namespace;
1156: }
1157:
1158: // From the pattern, find the namespace from which we are importing
1159: // and get the simple pattern (no namespace qualifiers or ::'s) at
1160: // the end.
1161:
1162: GetNamespaceForQualNameResult gnfqnr = interp.getnfqnResult;
1163: getNamespaceForQualName(interp, pattern, ns, TCL.LEAVE_ERR_MSG,
1164: gnfqnr);
1165: importNs = gnfqnr.ns;
1166: actualCtx = gnfqnr.actualCxt;
1167: simplePattern = gnfqnr.simpleName;
1168:
1169: // FIXME : the above call passes TCL.LEAVE_ERR_MSG, but
1170: // it seems like this will be a problem when exception is raised!
1171: if (importNs == null) {
1172: throw new TclException(interp,
1173: "unknown namespace in namespace forget pattern \""
1174: + pattern + "\"");
1175: }
1176:
1177: // Scan through the command table in the source namespace and look for
1178: // exported commands that match the string pattern. If the current
1179: // namespace has an imported command that refers to one of those real
1180: // commands, delete it. The importNs.cmdTable should not change during
1181: // this iteration.
1182:
1183: for (Iterator iter = importNs.cmdTable.entrySet().iterator(); iter
1184: .hasNext();) {
1185: Map.Entry entry = (Map.Entry) iter.next();
1186: cmdName = (String) entry.getKey();
1187:
1188: if (Util.stringMatch(cmdName, simplePattern)) {
1189: cmd = (WrappedCommand) ns.cmdTable.get(cmdName);
1190: if (cmd != null) { // cmd of same name in current namespace
1191: if (cmd.cmd instanceof ImportedCmdData) {
1192: interp.deleteCommandFromToken(cmd);
1193: }
1194: }
1195: }
1196: }
1197: return;
1198: }
1199:
1200: /*
1201: *----------------------------------------------------------------------
1202: *
1203: * TclGetOriginalCommand -> getOriginalCommand
1204: *
1205: * An imported command is created in a namespace when a "real" command
1206: * is imported from another namespace. If the specified command is an
1207: * imported command, this procedure returns the original command it
1208: * refers to.
1209: *
1210: * Results:
1211: * If the command was imported into a sequence of namespaces a, b,...,n
1212: * where each successive namespace just imports the command from the
1213: * previous namespace, this procedure returns the Tcl_Command token in
1214: * the first namespace, a. Otherwise, if the specified command is not
1215: * an imported command, the procedure returns null.
1216: *
1217: * Side effects:
1218: * None.
1219: *
1220: *----------------------------------------------------------------------
1221: */
1222:
1223: public static WrappedCommand getOriginalCommand(
1224: WrappedCommand command // The imported command for which the original
1225: // command should be returned.
1226: ) {
1227: WrappedCommand cmd = command;
1228: ImportedCmdData data;
1229:
1230: if (!(cmd.cmd instanceof ImportedCmdData)) {
1231: return null;
1232: }
1233:
1234: while (cmd.cmd instanceof ImportedCmdData) {
1235: data = (ImportedCmdData) cmd.cmd;
1236: cmd = data.realCmd;
1237: }
1238: return cmd;
1239: }
1240:
1241: /*
1242: *----------------------------------------------------------------------
1243: *
1244: * InvokeImportedCmd -> invokeImportedCmd
1245: *
1246: * Invoked by Tcl whenever the user calls an imported command that
1247: * was created by Tcl_Import. Finds the "real" command (in another
1248: * namespace), and passes control to it.
1249: *
1250: * Results:
1251: * Returns if successful, raises TclException if something goes wrong.
1252: *
1253: * Side effects:
1254: * Returns a result in the interpreter's result object. If anything
1255: * goes wrong, the result object is set to an error message.
1256: *
1257: *----------------------------------------------------------------------
1258: */
1259:
1260: static void invokeImportedCmd(Interp interp, // Current interpreter.
1261: ImportedCmdData data, // The data object for this imported command
1262: TclObject[] objv // Argument objects
1263: ) throws TclException {
1264: WrappedCommand realCmd = data.realCmd;
1265: realCmd.cmd.cmdProc(interp, objv);
1266: }
1267:
1268: /*
1269: *----------------------------------------------------------------------
1270: *
1271: * DeleteImportedCmd -> deleteImportedCmd
1272: *
1273: * Invoked by Tcl whenever an imported command is deleted. The "real"
1274: * command keeps a list of all the imported commands that refer to it,
1275: * so those imported commands can be deleted when the real command is
1276: * deleted. This procedure removes the imported command reference from
1277: * the real command's list, and frees up the memory associated with
1278: * the imported command.
1279: *
1280: * Results:
1281: * None.
1282: *
1283: * Side effects:
1284: * Removes the imported command from the real command's import list.
1285: *
1286: *----------------------------------------------------------------------
1287: */
1288:
1289: static void deleteImportedCmd(ImportedCmdData data) // The data object for this imported command
1290: {
1291: WrappedCommand realCmd = data.realCmd;
1292: WrappedCommand self = data.self;
1293: ImportRef ref, prev;
1294:
1295: prev = null;
1296: for (ref = realCmd.importRef; ref != null; ref = ref.next) {
1297: if (ref.importedCmd == self) {
1298: // Remove ref from real command's list of imported commands
1299: // that refer to it.
1300:
1301: if (prev == null) { // ref is first in list
1302: realCmd.importRef = ref.next;
1303: } else {
1304: prev.next = ref.next;
1305: }
1306: ref = null;
1307: data = null;
1308: return;
1309: }
1310: prev = ref;
1311: }
1312:
1313: throw new TclRuntimeError(
1314: "DeleteImportedCmd: did not find cmd in real cmd's list of import references");
1315: }
1316:
1317: /*
1318: *----------------------------------------------------------------------
1319: *
1320: * TclGetNamespaceForQualName -> getNamespaceForQualName
1321: *
1322: * Given a qualified name specifying a command, variable, or namespace,
1323: * and a namespace in which to resolve the name, this procedure returns
1324: * a pointer to the namespace that contains the item. A qualified name
1325: * consists of the "simple" name of an item qualified by the names of
1326: * an arbitrary number of containing namespace separated by "::"s. If
1327: * the qualified name starts with "::", it is interpreted absolutely
1328: * from the global namespace. Otherwise, it is interpreted relative to
1329: * the namespace specified by cxtNs if it is non-null. If cxtNs
1330: * is null, the name is interpreted relative to the current namespace.
1331: *
1332: * A relative name like "foo::bar::x" can be found starting in either
1333: * the current namespace or in the global namespace. So each search
1334: * usually follows two tracks, and two possible namespaces are
1335: * returned. If the procedure sets either gnfqnr.ns or gnfqnr.altNs to
1336: * null, then that path failed.
1337: *
1338: * If "flags" contains TCL.GLOBAL_ONLY, the relative qualified name is
1339: * sought only in the global :: namespace. The alternate search
1340: * (also) starting from the global namespace is ignored and
1341: * gnfqnr.altNs is set null.
1342: *
1343: * If "flags" contains TCL.NAMESPACE_ONLY, the relative qualified
1344: * name is sought only in the namespace specified by cxtNs. The
1345: * alternate search starting from the global namespace is ignored and
1346: * gnfqnr.altNs is set null. If both TCL.GLOBAL_ONLY and
1347: * TCL.NAMESPACE_ONLY are specified, TCL.GLOBAL_ONLY is ignored and
1348: * the search starts from the namespace specified by cxtNs.
1349: *
1350: * If "flags" contains CREATE_NS_IF_UNKNOWN, all namespace
1351: * components of the qualified name that cannot be found are
1352: * automatically created within their specified parent. This makes sure
1353: * that functions like Tcl_CreateCommand always succeed. There is no
1354: * alternate search path, so gnfqnr.altNs is set null.
1355: *
1356: * If "flags" contains FIND_ONLY_NS, the qualified name is treated as a
1357: * reference to a namespace, and the entire qualified name is
1358: * followed. If the name is relative, the namespace is looked up only
1359: * in the current namespace. A pointer to the namespace is stored in
1360: * gnfqnr.ns and null is stored in gnfqnr.simpleName. Otherwise, if
1361: * FIND_ONLY_NS is not specified, only the leading components are
1362: * treated as namespace names, and a pointer to the simple name of the
1363: * final component is stored in gnfqnr.simpleName.
1364: *
1365: * Results:
1366: * It sets gnfqnr.ns and gnfqnr.altNs to point to the two possible
1367: * namespaces which represent the last (containing) namespace in the
1368: * qualified name. If the procedure sets either gnfqnr.ns or gnfqnr.altNs
1369: * to null, then the search along that path failed. The procedure also
1370: * stores a pointer to the simple name of the final component in
1371: * gnfqnr.simpleName. If the qualified name is "::" or was treated as a
1372: * namespace reference (FIND_ONLY_NS), the procedure stores a pointer
1373: * to the namespace in gnfqnr.ns, null in gnfqnr.altNs, and sets
1374: * gnfqnr.simpleName to an empty string.
1375: *
1376: * If there is an error, this procedure returns TCL_ERROR. If "flags"
1377: * contains TCL_LEAVE_ERR_MSG, an error message is returned in the
1378: * interpreter's result object. Otherwise, the interpreter's result
1379: * object is left unchanged.
1380: *
1381: * gnfqnr.actualCxt is set to the actual context namespace. It is
1382: * set to the input context namespace pointer in cxtNs. If cxtNs
1383: * is null, it is set to the current namespace context. Note that
1384: * the GetNamespaceForQualNameResult result object is defined below.
1385: *
1386: * Side effects:
1387: * If "flags" contains CREATE_NS_IF_UNKNOWN, new namespaces may be
1388: * created.
1389: *
1390: *----------------------------------------------------------------------
1391: */
1392:
1393: static class GetNamespaceForQualNameResult {
1394: Namespace ns;
1395: Namespace altNs;
1396: Namespace actualCxt;
1397: String simpleName;
1398: }
1399:
1400: static void getNamespaceForQualName(Interp interp, // Interpreter in which to find the
1401: // namespace containing qualName.
1402: String qualName, // A namespace-qualified name of an
1403: // command, variable, or namespace.
1404: Namespace cxtNs, // The namespace in which to start the
1405: // search for qualName's namespace. If null
1406: // start from the current namespace.
1407: // Ignored if TCL.GLOBAL_ONLY is set.
1408: int flags, // Flags controlling the search: an OR'd
1409: // combination of TCL.GLOBAL_ONLY,
1410: // TCL.NAMESPACE_ONLY,
1411: // CREATE_NS_IF_UNKNOWN, and
1412: // FIND_ONLY_NS.
1413: GetNamespaceForQualNameResult gnfqnr)
1414:
1415: // gnfqnr.ns // Where this procedure stores a pointer
1416: // to containing namespace if qualName is
1417: // found starting from cxtNs or, if
1418: // TCL_GLOBAL_ONLY is set, if qualName is
1419: // found in the global :: namespace. null
1420: // is stored otherwise. This is an array
1421: // of length 1, value is stored at index 0
1422: // gnfqnr.altNs // Where this procedure stores a pointer
1423: // to containing namespace if qualName is
1424: // found starting from the global ::
1425: // namespace. null is stored if qualName
1426: // isn't found starting from :: or if the
1427: // TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY,
1428: // CREATE_NS_IF_UNKNOWN, FIND_ONLY_NS flag
1429: // is set. This is an array of length 1.
1430: // The value is stored at index 0
1431: // gnfqnr.actualCxt // Address where procedure stores a pointer
1432: // to the actual namespace from which the
1433: // search started. This is either cxtNs,
1434: // the :: namespace if TCL_GLOBAL_ONLY was
1435: // specified, or the current namespace if
1436: // cxtNs was null. This is an array of
1437: // length 1. The value is stored at index 0.
1438: // gnfqnr.simpleName // Where this procedure stores the
1439: // simple name at end of the qualName, or
1440: // null if qualName is "::" or the flag
1441: // FIND_ONLY_NS was specified. This is an
1442: // array of length 1, with value at index 0
1443: {
1444: gnfqnr.ns = null;
1445: gnfqnr.altNs = null;
1446: gnfqnr.actualCxt = null;
1447: gnfqnr.simpleName = null;
1448:
1449: Namespace ns = cxtNs;
1450: Namespace altNs;
1451: Namespace globalNs = getGlobalNamespace(interp);
1452: Namespace entryNs;
1453: String start, end;
1454: String nsName;
1455: int len;
1456: int start_ind, end_ind, name_len;
1457:
1458: // Determine the context namespace ns in which to start the primary
1459: // search. If the qualName name starts with a "::" or TCL_GLOBAL_ONLY
1460: // was specified, search from the global namespace. Otherwise, use the
1461: // namespace given in cxtNs, or if that is null, use the current
1462: // namespace context. Note that we always treat two or more
1463: // adjacent ":"s as a namespace separator.
1464:
1465: if ((flags & TCL.GLOBAL_ONLY) != 0) {
1466: ns = globalNs;
1467: } else if (ns == null) {
1468: if (interp.varFrame != null) {
1469: ns = interp.varFrame.ns;
1470: } else {
1471: ns = interp.globalNs;
1472: }
1473: }
1474:
1475: start_ind = 0;
1476: name_len = qualName.length();
1477:
1478: if ((name_len >= 2) && (qualName.charAt(0) == ':')
1479: && (qualName.charAt(1) == ':')) {
1480: start_ind = 2; // skip over the initial ::
1481:
1482: while ((start_ind < name_len)
1483: && (qualName.charAt(start_ind) == ':')) {
1484: start_ind++; // skip over a subsequent :
1485: }
1486:
1487: ns = globalNs;
1488: if (start_ind >= name_len) { // qualName is just two or more ":"s
1489: gnfqnr.ns = globalNs;
1490: gnfqnr.altNs = null;
1491: gnfqnr.actualCxt = globalNs;
1492: gnfqnr.simpleName = "";
1493: return;
1494: }
1495: }
1496: gnfqnr.actualCxt = ns;
1497:
1498: // Start an alternate search path starting with the global namespace.
1499: // However, if the starting context is the global namespace, or if the
1500: // flag is set to search only the namespace cxtNs, ignore the
1501: // alternate search path.
1502:
1503: altNs = globalNs;
1504: if ((ns == globalNs)
1505: || ((flags & (TCL.NAMESPACE_ONLY | FIND_ONLY_NS)) != 0)) {
1506: altNs = null;
1507: }
1508:
1509: // Loop to resolve each namespace qualifier in qualName.
1510:
1511: end_ind = start_ind;
1512:
1513: while (start_ind < name_len) {
1514: // Find the next namespace qualifier (i.e., a name ending in "::")
1515: // or the end of the qualified name (i.e., a name ending in "\0").
1516: // Set len to the number of characters, starting from start,
1517: // in the name; set end to point after the "::"s or at the "\0".
1518:
1519: len = 0;
1520: for (end_ind = start_ind; end_ind < name_len; end_ind++) {
1521: if (((name_len - end_ind) > 1)
1522: && (qualName.charAt(end_ind) == ':')
1523: && (qualName.charAt(end_ind + 1) == ':')) {
1524: end_ind += 2; // skip over the initial ::
1525: while ((end_ind < name_len)
1526: && (qualName.charAt(end_ind) == ':')) {
1527: end_ind++; // skip over a subsequent :
1528: }
1529: break;
1530: }
1531: len++;
1532: }
1533:
1534: if ((end_ind == name_len)
1535: && !((end_ind - start_ind >= 2) && ((qualName
1536: .charAt(end_ind - 1) == ':') && (qualName
1537: .charAt(end_ind - 2) == ':')))) {
1538:
1539: // qualName ended with a simple name at start. If FIND_ONLY_NS
1540: // was specified, look this up as a namespace. Otherwise,
1541: // start is the name of a cmd or var and we are done.
1542:
1543: if ((flags & FIND_ONLY_NS) != 0) {
1544: // assign the string from start_ind to the end of the name string
1545: nsName = qualName.substring(start_ind);
1546: } else {
1547: gnfqnr.ns = ns;
1548: gnfqnr.altNs = altNs;
1549: gnfqnr.simpleName = qualName.substring(start_ind);
1550: return;
1551: }
1552: } else {
1553: // start points to the beginning of a namespace qualifier ending
1554: // in "::". Create new string with the namespace qualifier.
1555:
1556: nsName = qualName.substring(start_ind, start_ind + len);
1557: }
1558:
1559: // Look up the namespace qualifier nsName in the current namespace
1560: // context. If it isn't found but CREATE_NS_IF_UNKNOWN is set,
1561: // create that qualifying namespace. This is needed for procedures
1562: // like Tcl_CreateCommand that cannot fail.
1563:
1564: if (ns != null) {
1565: entryNs = (Namespace) ns.childTable.get(nsName);
1566: if (entryNs != null) {
1567: ns = entryNs;
1568: } else if ((flags & CREATE_NS_IF_UNKNOWN) != 0) {
1569: CallFrame frame = interp.newCallFrame();
1570:
1571: pushCallFrame(interp, frame, ns, false);
1572: ns = createNamespace(interp, nsName, null);
1573:
1574: popCallFrame(interp);
1575: if (ns == null) {
1576: throw new RuntimeException(
1577: "Could not create namespace " + nsName);
1578: }
1579: } else {
1580: ns = null; // namespace not found and wasn't created
1581: }
1582: }
1583:
1584: // Look up the namespace qualifier in the alternate search path too.
1585:
1586: if (altNs != null) {
1587: altNs = (Namespace) altNs.childTable.get(nsName);
1588: }
1589:
1590: // If both search paths have failed, return null results.
1591:
1592: if ((ns == null) && (altNs == null)) {
1593: gnfqnr.ns = null;
1594: gnfqnr.altNs = null;
1595: gnfqnr.simpleName = null;
1596: return;
1597: }
1598:
1599: start_ind = end_ind;
1600: }
1601:
1602: // We ignore trailing "::"s in a namespace name, but in a command or
1603: // variable name, trailing "::"s refer to the cmd or var named {}.
1604:
1605: if (((flags & FIND_ONLY_NS) != 0)
1606: || ((end_ind > start_ind) && (qualName
1607: .charAt(end_ind - 1) != ':'))) {
1608: gnfqnr.simpleName = null; // found namespace name
1609: } else {
1610: // FIXME : make sure this does not throw exception when end_ind is at the end of the string
1611: gnfqnr.simpleName = qualName.substring(end_ind); // found cmd/var: points to empty string
1612: }
1613:
1614: // As a special case, if we are looking for a namespace and qualName
1615: // is "" and the current active namespace (ns) is not the global
1616: // namespace, return null (no namespace was found). This is because
1617: // namespaces can not have empty names except for the global namespace.
1618:
1619: if (((flags & FIND_ONLY_NS) != 0) && (name_len == 0)
1620: && (ns != globalNs)) {
1621: ns = null;
1622: }
1623:
1624: gnfqnr.ns = ns;
1625: gnfqnr.altNs = altNs;
1626: return;
1627: }
1628:
1629: /*
1630: *----------------------------------------------------------------------
1631: *
1632: * Tcl_FindNamespace -> findNamespace
1633: *
1634: * Searches for a namespace.
1635: *
1636: * Results:
1637: * Returns a reference to the namespace if it is found. Otherwise,
1638: * returns null and leaves an error message in the interpreter's
1639: * result object if "flags" contains TCL.LEAVE_ERR_MSG.
1640: *
1641: * Side effects:
1642: * None.
1643: *
1644: *----------------------------------------------------------------------
1645: */
1646:
1647: public static Namespace findNamespace(Interp interp, // The interpreter in which to find the
1648: // namespace.
1649: String name, // Namespace name. If it starts with "::",
1650: // will be looked up in global namespace.
1651: // Else, looked up first in contextNs
1652: // (current namespace if contextNs is
1653: // null), then in global namespace.
1654: Namespace contextNs, // Ignored if TCL.GLOBAL_ONLY flag is set
1655: // or if the name starts with "::".
1656: // Otherwise, points to namespace in which
1657: // to resolve name; if null, look up name
1658: // in the current namespace.
1659: int flags) // Flags controlling namespace lookup: an
1660: // OR'd combination of TCL.GLOBAL_ONLY and
1661: // TCL.LEAVE_ERR_MSG flags.
1662: {
1663: Namespace ns;
1664:
1665: // Find the namespace(s) that contain the specified namespace name.
1666: // Add the FIND_ONLY_NS flag to resolve the name all the way down
1667: // to its last component, a namespace.
1668:
1669: GetNamespaceForQualNameResult gnfqnr = interp.getnfqnResult;
1670: getNamespaceForQualName(interp, name, contextNs,
1671: (flags | FIND_ONLY_NS), gnfqnr);
1672: ns = gnfqnr.ns;
1673:
1674: if (ns != null) {
1675: return ns;
1676: } else if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
1677: /*
1678: interp.resetResult();
1679: TclString.append(interp.getResult(), "unknown namespace \"" + name + "\"");
1680: */
1681:
1682: // FIXME : is there a test case for this error?
1683: interp.setResult("unknown namespace \"" + name + "\"");
1684: }
1685: return null;
1686: }
1687:
1688: /*
1689: *----------------------------------------------------------------------
1690: *
1691: * Tcl_FindCommand -> findCommand
1692: *
1693: * Searches for a command.
1694: *
1695: * Results:
1696: * Returns a token for the command if it is found. Otherwise, if it
1697: * can't be found or there is an error, returns null and leaves an
1698: * error message in the interpreter's result object if "flags"
1699: * contains TCL.LEAVE_ERR_MSG.
1700: *
1701: * Side effects:
1702: * None.
1703: *
1704: *----------------------------------------------------------------------
1705: */
1706:
1707: public static WrappedCommand findCommand(Interp interp, // The interpreter in which to find the
1708: // command.
1709: String name, // Command's name. If it starts with "::",
1710: // will be looked up in global namespace.
1711: // Else, looked up first in contextNs
1712: // (current namespace if contextNs is
1713: // null), then in global namespace.
1714: Namespace contextNs, // Ignored if TCL.GLOBAL_ONLY flag set.
1715: // Otherwise, points to namespace in which
1716: // to resolve name. If null, look up name
1717: // in the current namespace.
1718: int flags // An OR'd combination of flags:
1719: // TCL.GLOBAL_ONLY (look up name only in
1720: // global namespace), TCL.NAMESPACE_ONLY
1721: // (look up only in contextNs, or the
1722: // current namespace if contextNs is
1723: // null), and TCL.LEAVE_ERR_MSG. If both
1724: // TCL.GLOBAL_ONLY and TCL.NAMESPACE_ONLY
1725: // are given, TCL.GLOBAL_ONLY is ignored.
1726: ) throws TclException {
1727: Interp.ResolverScheme res;
1728: Namespace cxtNs;
1729: Namespace ns0, ns1;
1730: String simpleName;
1731: int search;
1732: WrappedCommand cmd;
1733:
1734: // If this namespace has a command resolver, then give it first
1735: // crack at the command resolution. If the interpreter has any
1736: // command resolvers, consult them next. The command resolver
1737: // procedures may return a Tcl_Command value, they may signal
1738: // to continue onward, or they may signal an error.
1739:
1740: if ((flags & TCL.GLOBAL_ONLY) != 0) {
1741: cxtNs = getGlobalNamespace(interp);
1742: } else if (contextNs != null) {
1743: cxtNs = contextNs;
1744: } else {
1745: cxtNs = getCurrentNamespace(interp);
1746: }
1747:
1748: if (cxtNs.resolver != null || interp.resolvers != null) {
1749: try {
1750: if (cxtNs.resolver != null) {
1751: cmd = cxtNs.resolver.resolveCmd(interp, name,
1752: cxtNs, flags);
1753: } else {
1754: cmd = null;
1755: }
1756:
1757: if (cmd == null && interp.resolvers != null) {
1758: for (ListIterator iter = interp.resolvers
1759: .listIterator(); cmd == null
1760: && iter.hasNext();) {
1761: res = (Interp.ResolverScheme) iter.next();
1762: cmd = res.resolver.resolveCmd(interp, name,
1763: cxtNs, flags);
1764: }
1765: }
1766:
1767: if (cmd != null) {
1768: return cmd;
1769: }
1770: } catch (TclException e) {
1771: return null;
1772: }
1773: }
1774:
1775: // Find the namespace(s) that contain the command.
1776:
1777: GetNamespaceForQualNameResult gnfqnr = interp.getnfqnResult;
1778: getNamespaceForQualName(interp, name, contextNs, flags, gnfqnr);
1779: ns0 = gnfqnr.ns;
1780: ns1 = gnfqnr.altNs;
1781: cxtNs = gnfqnr.actualCxt;
1782: simpleName = gnfqnr.simpleName;
1783:
1784: // Look for the command in the command table of its namespace.
1785: // Be sure to check both possible search paths: from the specified
1786: // namespace context and from the global namespace.
1787:
1788: cmd = null;
1789: for (search = 0; (search < 2) && (cmd == null); search++) {
1790: Namespace ns;
1791: if (search == 0) {
1792: ns = ns0;
1793: } else if (search == 1) {
1794: ns = ns1;
1795: } else {
1796: throw new TclRuntimeError("bad search value" + search);
1797: }
1798: if ((ns != null) && (simpleName != null)) {
1799: cmd = (WrappedCommand) ns.cmdTable.get(simpleName);
1800: }
1801: }
1802: if (cmd != null) {
1803: return cmd;
1804: } else if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
1805: throw new TclException(interp, "unknown command \"" + name
1806: + "\"");
1807: }
1808:
1809: return null;
1810: }
1811:
1812: /*
1813: *----------------------------------------------------------------------
1814: *
1815: * Tcl_FindNamespaceVar -> findNamespaceVar
1816: *
1817: * Searches for a namespace variable, a variable not local to a
1818: * procedure. The variable can be either a scalar or an array, but
1819: * may not be an element of an array.
1820: *
1821: * Results:
1822: * Returns a token for the variable if it is found. Otherwise, if it
1823: * can't be found or there is an error, returns null and leaves an
1824: * error message in the interpreter's result object if "flags"
1825: * contains TCL.LEAVE_ERR_MSG.
1826: *
1827: * Side effects:
1828: * None.
1829: *
1830: *----------------------------------------------------------------------
1831: */
1832:
1833: public static Var findNamespaceVar(Interp interp, // The interpreter in which to find the
1834: // variable.
1835: String name, // Variable's name. name. If it starts with "::",
1836: // will be looked up in global namespace.
1837: // Else, looked up first in contextNs
1838: // (current namespace if contextNs is
1839: // null), then in global namespace.
1840: Namespace contextNs, // Ignored if TCL.GLOBAL_ONLY flag set.
1841: // Otherwise, points to namespace in which
1842: // to resolve name. If null, look up name
1843: // in the current namespace.
1844: int flags // An OR'd combination of flags:
1845: // TCL.GLOBAL_ONLY (look up name only in
1846: // global namespace), TCL.NAMESPACE_ONLY
1847: // (look up only in contextNs, or the
1848: // current namespace if contextNs is
1849: // null), and TCL.LEAVE_ERR_MSG. If both
1850: // TCL.GLOBAL_ONLY and TCL.NAMESPACE_ONLY
1851: // are given, TCL.GLOBAL_ONLY is ignored.
1852: ) throws TclException {
1853: Interp.ResolverScheme res;
1854: Namespace cxtNs;
1855: Namespace ns0, ns1;
1856: String simpleName;
1857: int search;
1858: Var var;
1859:
1860: // If this namespace has a variable resolver, then give it first
1861: // crack at the variable resolution. It may return a Tcl_Var
1862: // value, it may signal to continue onward, or it may signal
1863: // an error.
1864:
1865: if ((flags & TCL.GLOBAL_ONLY) != 0) {
1866: cxtNs = getGlobalNamespace(interp);
1867: } else if (contextNs != null) {
1868: cxtNs = contextNs;
1869: } else {
1870: cxtNs = getCurrentNamespace(interp);
1871: }
1872:
1873: if (cxtNs.resolver != null || interp.resolvers != null) {
1874: try {
1875: if (cxtNs.resolver != null) {
1876: var = cxtNs.resolver.resolveVar(interp, name,
1877: cxtNs, flags);
1878: } else {
1879: var = null;
1880: }
1881:
1882: if (var == null && interp.resolvers != null) {
1883: for (ListIterator iter = interp.resolvers
1884: .listIterator(); var == null
1885: && iter.hasNext();) {
1886: res = (Interp.ResolverScheme) iter.next();
1887: var = res.resolver.resolveVar(interp, name,
1888: cxtNs, flags);
1889: }
1890: }
1891:
1892: if (var != null) {
1893: return var;
1894: }
1895: } catch (TclException e) {
1896: return null;
1897: }
1898: }
1899:
1900: // Find the namespace(s) that contain the variable.
1901:
1902: GetNamespaceForQualNameResult gnfqnr = interp.getnfqnResult;
1903: getNamespaceForQualName(interp, name, contextNs, flags, gnfqnr);
1904: ns0 = gnfqnr.ns;
1905: ns1 = gnfqnr.altNs;
1906: cxtNs = gnfqnr.actualCxt;
1907: simpleName = gnfqnr.simpleName;
1908:
1909: // Look for the variable in the variable table of its namespace.
1910: // Be sure to check both possible search paths: from the specified
1911: // namespace context and from the global namespace.
1912:
1913: var = null;
1914: for (search = 0; (search < 2) && (var == null); search++) {
1915: Namespace ns;
1916: if (search == 0) {
1917: ns = ns0;
1918: } else if (search == 1) {
1919: ns = ns1;
1920: } else {
1921: throw new TclRuntimeError("bad search value" + search);
1922: }
1923: if ((ns != null) && (simpleName != null)) {
1924: var = (Var) ns.varTable.get(simpleName);
1925: }
1926: }
1927: if (var != null) {
1928: return var;
1929: } else if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
1930: /*
1931: interp.resetResult();
1932: TclString.append(interp.getResult(), "unknown variable \"" + name + "\"");
1933: */
1934:
1935: // FIXME : is there a test case for this error?
1936: interp.setResult("unknown variable \"" + name + "\"");
1937: }
1938: return null;
1939: }
1940:
1941: /*
1942: *----------------------------------------------------------------------
1943: *
1944: * TclResetShadowedCmdRefs -> resetShadowedCmdRefs
1945: *
1946: * Called when a command is added to a namespace to check for existing
1947: * command references that the new command may invalidate. Consider the
1948: * following cases that could happen when you add a command "foo" to a
1949: * namespace "b":
1950: * 1. It could shadow a command named "foo" at the global scope.
1951: * If it does, all command references in the namespace "b" are
1952: * suspect.
1953: * 2. Suppose the namespace "b" resides in a namespace "a".
1954: * Then to "a" the new command "b::foo" could shadow another
1955: * command "b::foo" in the global namespace. If so, then all
1956: * command references in "a" are suspect.
1957: * The same checks are applied to all parent namespaces, until we
1958: * reach the global :: namespace.
1959: *
1960: * Results:
1961: * None.
1962: *
1963: * Side effects:
1964: * If the new command shadows an existing command, then the
1965: * epoch for each command in each namespace that sees the
1966: * shadow is incremented. This invalidates any command caches
1967: * inside each command in the namespace. The next time the
1968: * command is used, cached refrences will be resolved from scratch.
1969: *
1970: *----------------------------------------------------------------------
1971: */
1972:
1973: static void resetShadowedCmdRefs(Interp interp, // Interpreter containing the new command
1974: WrappedCommand newCmd) // Command added to a namespace
1975: {
1976: String cmdName;
1977: Namespace ns, tmpNs;
1978: Namespace trailNs, shadowNs;
1979: Namespace globalNs = getGlobalNamespace(interp);
1980: int i;
1981: boolean found;
1982: WrappedCommand wcmd;
1983:
1984: // Array used to hold the trail list.
1985:
1986: Namespace[] trailArray = null;
1987: int trailFront = -1;
1988: int trailSize = NUM_TRAIL_ELEMS;
1989:
1990: // Start at the namespace containing the new command, and work up
1991: // through the list of parents. Stop just before the global namespace,
1992: // since the global namespace can't "shadow" its own entries.
1993: //
1994: // The namespace "trail" list we build consists of the names of each
1995: // namespace that encloses the new command, in order from outermost to
1996: // innermost: for example, "a" then "b". Each iteration of this loop
1997: // eventually extends the trail upwards by one namespace, ns. We use
1998: // this trail list to see if ns (e.g. "a" in 2. above) could have
1999: // now-invalid cached command references. This will happen if ns
2000: // (e.g. "a") contains a sequence of child namespaces (e.g. "b")
2001: // such that there is a identically-named sequence of child namespaces
2002: // starting from :: (e.g. "::b") whose tail namespace contains a command
2003: // also named cmdName.
2004:
2005: cmdName = newCmd.hashKey;
2006: for (ns = newCmd.ns; (ns != null) && (ns != globalNs); ns = ns.parent) {
2007: // Find the maximal sequence of child namespaces contained in ns
2008: // such that there is a identically-named sequence of child
2009: // namespaces starting from ::. shadowNs will be the tail of this
2010: // sequence, or the deepest namespace under :: that might contain a
2011: // command now shadowed by cmdName. We check below if shadowNs
2012: // actually contains a command cmdName.
2013:
2014: found = true;
2015: shadowNs = globalNs;
2016:
2017: for (i = trailFront; i >= 0; i--) {
2018: trailNs = trailArray[i];
2019: tmpNs = (Namespace) shadowNs.childTable
2020: .get(trailNs.name);
2021: if (tmpNs != null) {
2022: shadowNs = tmpNs;
2023: } else {
2024: found = false;
2025: break;
2026: }
2027: }
2028:
2029: // If shadowNs contains a command named cmdName, we invalidate
2030: // all of the command refs cached in ns. As a boundary case,
2031: // shadowNs is initially :: and we check for case 1. above.
2032:
2033: if (found) {
2034: wcmd = (WrappedCommand) shadowNs.cmdTable.get(cmdName);
2035: if (wcmd != null) {
2036: // Invalidate cached command ref in each command
2037: // defined in this namespace.
2038:
2039: //nsPtr->cmdRefEpoch++;
2040:
2041: for (Iterator iter = ns.cmdTable.entrySet()
2042: .iterator(); iter.hasNext();) {
2043: Map.Entry entry = (Map.Entry) iter.next();
2044: wcmd = (WrappedCommand) entry.getValue();
2045: wcmd.incrEpoch();
2046: }
2047: }
2048: }
2049:
2050: // Insert ns at the front of the trail list: i.e., at the end
2051: // of the trail array.
2052:
2053: if (trailArray == null) {
2054: trailArray = new Namespace[NUM_TRAIL_ELEMS];
2055: }
2056:
2057: trailFront++;
2058: if (trailFront == trailSize) {
2059: int size = trailSize * 2;
2060: Namespace[] tmp = new Namespace[size];
2061: System.arraycopy(trailArray, 0, tmp, 0,
2062: trailArray.length);
2063: trailArray = tmp;
2064: trailSize = size;
2065: }
2066: trailArray[trailFront] = ns;
2067: }
2068: }
2069:
2070: /**
2071: *----------------------------------------------------------------------
2072: *
2073: * Tcl_SetNamespaceResolvers -> setNamespaceResolver
2074: *
2075: * Sets the command/variable resolution object for a namespace,
2076: * thereby changing the way that command/variable names are
2077: * interpreted. This allows extension writers to support different
2078: * name resolution schemes, such as those for object-oriented
2079: * packages.
2080: *
2081: * Command resolution is handled by the following method:
2082: *
2083: * resolveCmd (Interp interp, String name,
2084: * Namespace context, int flags)
2085: * throws TclException;
2086: *
2087: * Whenever a command is executed or Namespace.findCommand is invoked
2088: * within the namespace, this method is called to resolve the
2089: * command name. If this method is able to resolve the name,
2090: * it should return the corresponding WrappedCommand. Otherwise,
2091: * the procedure can return null, and the command will
2092: * be treated under the usual name resolution rules. Or, it can
2093: * throw a TclException, and the command will be considered invalid.
2094: *
2095: * Variable resolution is handled by the following method:
2096: *
2097: * resolveVar (Interp interp, String name,
2098: * Namespace context, int flags)
2099: * throws TclException;
2100: *
2101: * If this method is able to resolve the name, it should return
2102: * the variable as Var object. The method may also
2103: * return null, and the variable will be treated under the usual
2104: * name resolution rules. Or, it can throw a TclException,
2105: * and the variable will be considered invalid.
2106: *
2107: * Results:
2108: * See above.
2109: *
2110: * Side effects:
2111: * None.
2112: *
2113: *----------------------------------------------------------------------
2114: */
2115:
2116: public static void setNamespaceResolver(Namespace namespace, // Namespace whose resolution rules
2117: // are being modified.
2118: Resolver resolver) // command and variable resolution
2119: {
2120: // Plug in the new command resolver.
2121:
2122: namespace.resolver = resolver;
2123: }
2124:
2125: /**
2126: *----------------------------------------------------------------------
2127: *
2128: * Tcl_GetNamespaceResolvers -> getNamespaceResolver
2129: *
2130: * Returns the current command/variable resolution object
2131: * for a namespace. By default, these objects are null.
2132: * New objects can be installed by calling setNamespaceResolver,
2133: * to provide new name resolution rules.
2134: *
2135: * Results:
2136: * Returns the esolver object assigned to this namespace.
2137: * Returns null otherwise.
2138: *
2139: * Side effects:
2140: * None.
2141: *
2142: *----------------------------------------------------------------------
2143: */
2144:
2145: static Resolver getNamespaceResolver(Namespace namespace) // Namespace whose resolution rules
2146: // are being queried.
2147: {
2148: return namespace.resolver;
2149: }
2150:
2151: /**
2152: *----------------------------------------------------------------------
2153: *
2154: * Tcl_FirstHashEntry -> FirstHashEntry
2155: *
2156: * Return the first Object value contained in the given table.
2157: * This method is used only when taking apart a table where
2158: * entries in the table could be removed elsewhere. An Iterator
2159: * is no longer valid once entries have been removed so it
2160: * is not possible to take a table apart safely with a single
2161: * iterator. This method returns null when there are no more
2162: * elements in the table, so it should not be used with a
2163: * table that contains null values. This method is not efficient,
2164: * but it is required when dealing with a Java Iterator when
2165: * the table being iterated could have elements added or deleted.
2166: *
2167: *----------------------------------------------------------------------
2168: */
2169:
2170: static Object FirstHashEntry(HashMap table) {
2171: Object retVal;
2172: Set eset = table.entrySet();
2173: if (eset.size() == 0) {
2174: return null;
2175: }
2176: Iterator iter = eset.iterator();
2177: if (!iter.hasNext()) {
2178: throw new TclRuntimeError(
2179: "no next() object but set size was " + eset.size());
2180: }
2181: Map.Entry entry = (Map.Entry) iter.next();
2182: retVal = entry.getValue();
2183: if (retVal == null) {
2184: throw new TclRuntimeError("entry value should not be null");
2185: }
2186: return retVal;
2187: }
2188:
2189: } // end class Namespace
|