0001: /*
0002: * Var.java
0003: *
0004: * Copyright (c) 1997 Sun Microsystems, Inc.
0005: *
0006: * See the file "license.terms" for information on usage and
0007: * redistribution of this file, and for a DISCLAIMER OF ALL
0008: * WARRANTIES.
0009: *
0010: * RCS: @(#) $Id: Var.java,v 1.34 2006/06/10 04:15:59 mdejong Exp $
0011: *
0012: */
0013: package tcl.lang;
0014:
0015: import java.util.*;
0016:
0017: /*
0018: * Implements variables in Tcl. The Var class encapsulates most of the functionality
0019: * of the methods in generic/tclVar.c and the structure Tcl_Var from the C version.
0020: */
0021:
0022: public class Var {
0023:
0024: /**
0025: * Flag bits for variables. The first three (SCALAR, ARRAY, and
0026: * LINK) are mutually exclusive and give the "type" of the variable.
0027: * UNDEFINED is independent of the variable's type. Note that
0028: * using an int field here instead of allocating 9 boolean
0029: * members makes the resulting object take up less memory.
0030: * If there were only 8 boolean fields then the size of
0031: * the Var object would be the same.
0032: *
0033: * SCALAR - 1 means this is a scalar variable and not
0034: * an array or link. The tobj field contains
0035: * the variable's value.
0036: * ARRAY - 1 means this is an array variable rather
0037: * than a scalar variable or link. The
0038: * arraymap field points to the array's
0039: * hashtable for its elements.
0040: * LINK - 1 means this Var structure contains a
0041: * reference to another Var structure that
0042: * either has the real value or is itself
0043: * another LINK pointer. Variables like
0044: * this come about through "upvar" and "global"
0045: * commands, or through references to variables
0046: * in enclosing namespaces.
0047: * UNDEFINED - 1 means that the variable is in the process
0048: * of being deleted. An undefined variable
0049: * logically does not exist and survives only
0050: * while it has a trace, or if it is a global
0051: * variable currently being used by some
0052: * procedure.
0053: * IN_HASHTABLE - 1 means this variable is in a hashtable. 0 if
0054: * a local variable that was assigned a slot
0055: * in a procedure frame by the compiler so the
0056: * Var storage is part of the call frame.
0057: * TRACE_EXISTS - 1 means that trace(s) exist on this
0058: * scalar or array variable. This flag is
0059: * set when (var.traces == null), it is
0060: * cleared when there are no more traces.
0061: * TRACE_ACTIVE - 1 means that trace processing is currently
0062: * underway for a read or write access, so
0063: * new read or write accesses should not cause
0064: * trace procedures to be called and the
0065: * variable can't be deleted.
0066: * ARRAY_ELEMENT - 1 means that this variable is an array
0067: * element, so it is not legal for it to be
0068: * an array itself (the ARRAY flag had
0069: * better not be set).
0070: * NAMESPACE_VAR - 1 means that this variable was declared
0071: * as a namespace variable. This flag ensures
0072: * it persists until its namespace is
0073: * destroyed or until the variable is unset;
0074: * it will persist even if it has not been
0075: * initialized and is marked undefined.
0076: * The variable's refCount is incremented to
0077: * reflect the "reference" from its namespace.
0078: * NO_CACHE - 1 means that code should not be able to hold
0079: * a cached reference to this variable. This flag
0080: * is only set for Var objects returned by
0081: * a namespace or interp resolver. It is not possible
0082: * to clear this flag, so the variable can't
0083: * be cached as long as it is alive.
0084: * NON_LOCAL - 1 means that the variable exists in the
0085: * compiled local table, but it is not a
0086: * local or imported local. This flag is
0087: * only set in compiled code when scoped
0088: * global var refs like $::myvar are found.
0089: * These variables are not considered part
0090: * of a variable frame and can't be found
0091: * at runtime.
0092: */
0093:
0094: static final int SCALAR = 0x1;
0095: static final int ARRAY = 0x2;
0096: static final int LINK = 0x4;
0097: static final int UNDEFINED = 0x8;
0098: static final int IN_HASHTABLE = 0x10;
0099: static final int TRACE_ACTIVE = 0x20;
0100: static final int ARRAY_ELEMENT = 0x40;
0101: static final int NAMESPACE_VAR = 0x80;
0102: static final int NO_CACHE = 0x100;
0103: static final int NON_LOCAL = 0x200;
0104: static final int TRACE_EXISTS = 0x400;
0105:
0106: // Flag used only with makeUpvar()
0107: static final int EXPLICIT_LOCAL_NAME = 0x1000;
0108:
0109: // Methods to read various flag bits of variables.
0110:
0111: final boolean isVarScalar() {
0112: return ((flags & SCALAR) != 0);
0113: }
0114:
0115: final boolean isVarLink() {
0116: return ((flags & LINK) != 0);
0117: }
0118:
0119: final boolean isVarArray() {
0120: return ((flags & ARRAY) != 0);
0121: }
0122:
0123: final boolean isVarUndefined() {
0124: return ((flags & UNDEFINED) != 0);
0125: }
0126:
0127: final boolean isVarArrayElement() {
0128: return ((flags & ARRAY_ELEMENT) != 0);
0129: }
0130:
0131: final boolean isVarNamespace() {
0132: return ((flags & NAMESPACE_VAR) != 0);
0133: }
0134:
0135: final boolean isVarInHashtable() {
0136: return ((flags & IN_HASHTABLE) != 0);
0137: }
0138:
0139: final boolean isVarTraceExists() {
0140: return ((flags & TRACE_EXISTS) != 0);
0141: }
0142:
0143: final boolean isVarNoCache() {
0144: return ((flags & NO_CACHE) != 0);
0145: }
0146:
0147: // True when a compiled local variable should
0148: // not be a member of the var frame.
0149:
0150: final boolean isVarNonLocal() {
0151: return ((flags & NON_LOCAL) != 0);
0152: }
0153:
0154: // Methods to ensure that various flag bits are set properly for variables.
0155:
0156: final void setVarScalar() {
0157: flags = (flags & ~(ARRAY | LINK)) | SCALAR;
0158: }
0159:
0160: final void setVarArray() {
0161: flags = (flags & ~(SCALAR | LINK)) | ARRAY;
0162: }
0163:
0164: final void setVarLink() {
0165: flags = (flags & ~(SCALAR | ARRAY)) | LINK;
0166: }
0167:
0168: final void setVarArrayElement() {
0169: flags = (flags & ~ARRAY) | ARRAY_ELEMENT;
0170: }
0171:
0172: final void setVarUndefined() {
0173: flags |= UNDEFINED;
0174: }
0175:
0176: final void setVarNamespace() {
0177: flags |= NAMESPACE_VAR;
0178: }
0179:
0180: final void setVarInHashtable() {
0181: flags |= IN_HASHTABLE;
0182: }
0183:
0184: final void setVarNonLocal() {
0185: flags |= NON_LOCAL;
0186: }
0187:
0188: final void setVarNoCache() {
0189: flags |= NO_CACHE;
0190: }
0191:
0192: final void setVarTraceExists() {
0193: flags |= TRACE_EXISTS;
0194: }
0195:
0196: final void clearVarUndefined() {
0197: flags &= ~UNDEFINED;
0198: }
0199:
0200: final void clearVarInHashtable() {
0201: flags &= ~IN_HASHTABLE;
0202: }
0203:
0204: final void clearVarTraceExists() {
0205: flags &= ~TRACE_EXISTS;
0206: }
0207:
0208: /**
0209: * A Var object is one of the following three types.
0210: *
0211: * <li>Scalar variable - tobj is the object stored in the var.
0212: * <li> Array variable - arraymap is the hashtable that stores
0213: * all the elements. <p>
0214: * <li> Upvar (Link) - linkto is the variable associated by this upvar.
0215: * </ul>
0216: */
0217:
0218: TclObject tobj;
0219: HashMap arraymap;
0220: Var linkto;
0221:
0222: /**
0223: * List that holds the traces that were placed in this Var
0224: */
0225:
0226: ArrayList traces;
0227:
0228: ArrayList sidVec;
0229:
0230: /**
0231: * Miscellaneous bits of information about variable.
0232: *
0233: * @see Var#SCALAR
0234: * @see Var#ARRAY
0235: * @see Var#LINK
0236: * @see Var#UNDEFINED
0237: * @see Var#IN_HASHTABLE
0238: * @see Var#TRACE_ACTIVE
0239: * @see Var#ARRAY_ELEMENT
0240: * @see Var#NAMESPACE_VAR
0241: */
0242:
0243: int flags;
0244:
0245: /**
0246: * If variable is in a hashtable, either the
0247: * hash table entry that refers to this
0248: * variable or null if the variable has been
0249: * detached from its hash table (e.g. an
0250: * array is deleted, but some of its
0251: * elements are still referred to in
0252: * upvars). null if the variable is not in a
0253: * hashtable. This is used to delete an
0254: * variable from its hashtable if it is no
0255: * longer needed.
0256: */
0257:
0258: HashMap table;
0259:
0260: /**
0261: * The key under which this variable is stored in the hash table.
0262: */
0263:
0264: String hashKey;
0265:
0266: /**
0267: * Counts number of active uses of this
0268: * variable, not including its entry in the
0269: * call frame or the hash table: 1 for each
0270: * additional variable whose link points
0271: * here, 1 for each nested trace active on
0272: * variable, and 1 if the variable is a
0273: * namespace variable. This record can't be
0274: * deleted until refCount becomes 0.
0275: */
0276:
0277: int refCount;
0278:
0279: /**
0280: * Reference to the namespace that contains
0281: * this variable. This is set only for namespace
0282: * variables. A local variable in a procedure
0283: * will always have a null ns field.
0284: */
0285:
0286: Namespace ns;
0287:
0288: /**
0289: * NewVar -> Var
0290: *
0291: * Construct a variable and initialize its fields.
0292: */
0293:
0294: Var() {
0295: tobj = null;
0296: arraymap = null;
0297: linkto = null;
0298: //name = null; // Like hashKey in Jacl
0299: ns = null;
0300: hashKey = null; // Like hPtr in the C implementation
0301: table = null; // Like hPtr in the C implementation
0302: refCount = 0;
0303: traces = null;
0304: //search = null;
0305: sidVec = null; // Like search in the C implementation
0306: flags = (SCALAR | UNDEFINED | IN_HASHTABLE);
0307: }
0308:
0309: /**
0310: * Used to create a String that describes this variable.
0311: */
0312:
0313: public String toString() {
0314: StringBuffer sb = new StringBuffer();
0315: if (ns != null) {
0316: sb.append(ns.fullName);
0317: if (ns.fullName.equals("::")) {
0318: sb.append(hashKey);
0319: } else {
0320: sb.append("::");
0321: sb.append(hashKey);
0322: }
0323: } else {
0324: sb.append(hashKey);
0325: }
0326:
0327: if (isVarScalar()) {
0328: sb.append(" ");
0329: sb.append("SCALAR");
0330: }
0331: if (isVarLink()) {
0332: sb.append(" ");
0333: sb.append("LINK");
0334: }
0335: if (isVarArray()) {
0336: sb.append(" ");
0337: sb.append("ARRAY");
0338: }
0339: if (isVarUndefined()) {
0340: sb.append(" ");
0341: sb.append("UNDEFINED");
0342: }
0343: if (isVarArrayElement()) {
0344: sb.append(" ");
0345: sb.append("ARRAY_ELEMENT");
0346: }
0347: if (isVarNamespace()) {
0348: sb.append(" ");
0349: sb.append("NAMESPACE_VAR");
0350: }
0351: if (isVarInHashtable()) {
0352: sb.append(" ");
0353: sb.append("IN_HASHTABLE");
0354: }
0355: if (isVarTraceExists()) {
0356: sb.append(" ");
0357: sb.append("TRACE_EXISTS");
0358: }
0359: if (isVarNoCache()) {
0360: sb.append(" ");
0361: sb.append("NO_CACHE");
0362: }
0363: return sb.toString();
0364: }
0365:
0366: /**
0367: * Used by ArrayCmd to create a unique searchId string. If the
0368: * sidVec List is empty then simply return 1. Else return 1
0369: * plus the SearchId.index value of the last Object in the vector.
0370: *
0371: * @param None
0372: * @return The int value for unique SearchId string.
0373: */
0374:
0375: protected int getNextIndex() {
0376: int size = sidVec.size();
0377: if (size == 0) {
0378: return 1;
0379: }
0380: SearchId sid = (SearchId) sidVec.get(size - 1);
0381: return (sid.getIndex() + 1);
0382: }
0383:
0384: /**
0385: * Find the SearchId that in the sidVec List that is equal the
0386: * unique String s and returns the iterator associated with
0387: * that SearchId.
0388: *
0389: * @param s String that ia a unique identifier for a SearchId object
0390: * @return Iterator if a match is found else null.
0391: */
0392:
0393: protected Iterator getSearch(String s) {
0394: SearchId sid;
0395: for (int i = 0; i < sidVec.size(); i++) {
0396: sid = (SearchId) sidVec.get(i);
0397: if (sid.equals(s)) {
0398: return sid.getIterator();
0399: }
0400: }
0401: return null;
0402: }
0403:
0404: /**
0405: * Find the SearchId object in the sidVec list and remove it.
0406: *
0407: * @param sid String that ia a unique identifier for a SearchId object.
0408: */
0409:
0410: protected boolean removeSearch(String sid) {
0411: SearchId curSid;
0412:
0413: for (int i = 0; i < sidVec.size(); i++) {
0414: curSid = (SearchId) sidVec.get(i);
0415: if (curSid.equals(sid)) {
0416: sidVec.remove(i);
0417: return true;
0418: }
0419: }
0420: return false;
0421: }
0422:
0423: // End of the instance method for the Var class, the rest of the methods
0424: // are Var related methods ported from the code in generic/tclVar.c
0425:
0426: // The strings below are used to indicate what went wrong when a
0427: // variable access is denied.
0428:
0429: static final String noSuchVar = "no such variable";
0430: static final String isArray = "variable is array";
0431: static final String needArray = "variable isn't array";
0432: static final String noSuchElement = "no such element in array";
0433: static final String danglingElement = "upvar refers to element in deleted array";
0434: static final String danglingVar = "upvar refers to variable in deleted namespace";
0435: static final String badNamespace = "parent namespace doesn't exist";
0436: static final String missingName = "missing variable name";
0437:
0438: // Return true if a variable name stored in a String
0439: // indicates an array element. For example, this
0440: // method would return true for "foo(bar) and false
0441: // for "foo".
0442:
0443: public static final boolean isArrayVarname(String varName) {
0444: final int lastInd = varName.length() - 1;
0445: if (varName.charAt(lastInd) == ')') {
0446: if (varName.indexOf('(') != -1) {
0447: return true;
0448: }
0449: }
0450: return false;
0451: }
0452:
0453: /**
0454: * TclLookupVar -> lookupVar
0455: *
0456: * This procedure is used by virtually all of the variable
0457: * code to locate a variable given its name(s).
0458: *
0459: * @param part1 if part2 isn't NULL, this is the name of an array.
0460: * Otherwise, this is a full variable name that could include
0461: * a parenthesized array elemnt or a scalar.
0462: * @param part2 Name of an element within array, or null.
0463: * @param flags Only the TCL.GLOBAL_ONLY bit matters.
0464: * @param msg Verb to use in error messages, e.g. "read" or "set".
0465: * @param create OR'ed combination of CRT_PART1 and CRT_PART2.
0466: * Tells which entries to create if they don't already exist.
0467: * @param throwException true if an exception should be throw if the
0468: * variable cannot be found.
0469: * @return a two element array. a[0] is the variable indicated by
0470: * part1 and part2, or null if the variable couldn't be
0471: * found and throwException is false.
0472: * <p>
0473: * If the variable is found, a[1] is the array that
0474: * contains the variable (or null if the variable is a scalar).
0475: * If the variable can't be found and either createPart1 or
0476: * createPart2 are true, a new as-yet-undefined (VAR_UNDEFINED)
0477: * variable instance is created, entered into a hash
0478: * table, and returned.
0479: * Note: it's possible that var.value of the returned variable
0480: * may be null (variable undefined), even if createPart1 or createPart2
0481: * are true (these only cause the hash table entry or array to be created).
0482: * For example, the variable might be a global that has been unset but
0483: * is still referenced by a procedure, or a variable that has been unset
0484: * but it only being kept in existence by a trace.
0485: * @exception TclException if the variable cannot be found and
0486: * throwException is true.
0487: *
0488: */
0489:
0490: static Var[] lookupVar(Interp interp, // Interpreter to use for lookup.
0491: String part1, // If part2 isn't null, this is the name of
0492: // an array. Otherwise, this
0493: // is a full variable name that could
0494: // include a parenthesized array element.
0495: String part2, // Name of element within array, or null.
0496: int flags, // Only TCL.GLOBAL_ONLY, TCL.NAMESPACE_ONLY,
0497: // and TCL.LEAVE_ERR_MSG bits matter.
0498: String msg, // Verb to use in error messages, e.g.
0499: // "read" or "set". Only needed if
0500: // TCL.LEAVE_ERR_MSG is set in flags.
0501: boolean createPart1, // If true, create hash table entry for part 1
0502: // of name, if it doesn't already exist. If
0503: // false, return error if it doesn't exist.
0504: boolean createPart2 // If true, create hash table entry for part 2
0505: // of name, if it doesn't already exist. If
0506: // false, throw exception if it doesn't exist.
0507: ) throws TclException {
0508: CallFrame varFrame = interp.varFrame;
0509: // Reference to the procedure call frame whose
0510: // variables are currently in use. Same as
0511: // the current procedure's frame, if any,
0512: // unless an "uplevel" is executing.
0513: HashMap table; // to the hashtable, if any, in which
0514: // to look up the variable.
0515: Var var; // Used to search for global names.
0516: String elName; // Name of array element or null.
0517: int openParen;
0518: // If this procedure parses a name into
0519: // array and index, these point to the
0520: // parens around the index. Otherwise they
0521: // are -1. These are needed to restore
0522: // the parens after parsing the name.
0523: Namespace varNs, cxtNs;
0524: Interp.ResolverScheme res;
0525:
0526: var = null;
0527: varNs = null; // set non-null if a nonlocal variable
0528:
0529: // Parse part1 into array name and index.
0530: // Always check if part1 is an array element name and allow it only if
0531: // part2 is not given.
0532: // (if one does not care about creating array elements that can't be used
0533: // from tcl, and prefer slightly better performance, one can put
0534: // the following in an if (part2 == null) { ... } block and remove
0535: // the part2's test and error reporting or move that code in array set)
0536:
0537: elName = part2;
0538: openParen = -1;
0539: int lastInd = part1.length() - 1;
0540: if ((lastInd > 0) && (part1.charAt(lastInd) == ')')) {
0541: openParen = part1.indexOf('(');
0542: }
0543: if (openParen != -1) {
0544: if (part2 != null) {
0545: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
0546: throw new TclVarException(interp, part1, part2,
0547: msg, needArray);
0548: }
0549: return null;
0550: }
0551: elName = part1.substring(openParen + 1, lastInd);
0552: part2 = elName; // same as elName, only used in error reporting
0553: part1 = part1.substring(0, openParen);
0554: }
0555:
0556: // If this namespace has a variable resolver, then give it first
0557: // crack at the variable resolution. It may return a Var
0558: // value, it may signal to continue onward, or it may signal
0559: // an error.
0560:
0561: if (((flags & TCL.GLOBAL_ONLY) != 0)
0562: || (interp.varFrame == null)) {
0563: cxtNs = interp.globalNs;
0564: } else {
0565: cxtNs = interp.varFrame.ns;
0566: }
0567:
0568: if (cxtNs.resolver != null || interp.resolvers != null) {
0569: try {
0570: if (cxtNs.resolver != null) {
0571: var = cxtNs.resolver.resolveVar(interp, part1,
0572: cxtNs, flags);
0573: if (var != null) {
0574: var.setVarNoCache();
0575: }
0576: } else {
0577: var = null;
0578: }
0579:
0580: if (var == null && interp.resolvers != null) {
0581: for (ListIterator iter = interp.resolvers
0582: .listIterator(); var == null
0583: && iter.hasNext();) {
0584: res = (Interp.ResolverScheme) iter.next();
0585: var = res.resolver.resolveVar(interp, part1,
0586: cxtNs, flags);
0587: if (var != null) {
0588: var.setVarNoCache();
0589: }
0590: }
0591: }
0592: } catch (TclException e) {
0593: var = null;
0594: }
0595: }
0596:
0597: // Look up part1. Look it up as either a namespace variable or as a
0598: // local variable in a procedure call frame (varFrame).
0599: // Interpret part1 as a namespace variable if:
0600: // 1) so requested by a TCL.GLOBAL_ONLY or TCL.NAMESPACE_ONLY flag,
0601: // 2) there is no active frame (we're at the global :: scope),
0602: // 3) the active frame was pushed to define the namespace context
0603: // for a "namespace eval" or "namespace inscope" command,
0604: // 4) the name has namespace qualifiers ("::"s).
0605: // Otherwise, if part1 is a local variable, search first in the
0606: // frame's array of compiler-allocated local variables, then in its
0607: // hashtable for runtime-created local variables.
0608: //
0609: // If createPart1 and the variable isn't found, create the variable and,
0610: // if necessary, create varFrame's local var hashtable.
0611:
0612: if (((flags & (TCL.GLOBAL_ONLY | TCL.NAMESPACE_ONLY)) != 0)
0613: || (varFrame == null) || !varFrame.isProcCallFrame
0614: || (part1.indexOf("::") != -1)) {
0615: String tail;
0616:
0617: // Don't pass TCL.LEAVE_ERR_MSG, we may yet create the variable,
0618: // or otherwise generate our own error!
0619:
0620: var = Namespace.findNamespaceVar(interp, part1, null, flags
0621: & ~TCL.LEAVE_ERR_MSG);
0622: if (var == null) {
0623: if (createPart1) { // var wasn't found so create it
0624:
0625: Namespace.GetNamespaceForQualNameResult gnfqnr = interp.getnfqnResult;
0626: Namespace.getNamespaceForQualName(interp, part1,
0627: null, flags, gnfqnr);
0628: varNs = gnfqnr.ns;
0629: tail = gnfqnr.simpleName;
0630:
0631: if (varNs == null) {
0632: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
0633: throw new TclVarException(interp, part1,
0634: part2, msg, badNamespace);
0635: }
0636: return null;
0637: }
0638: if (tail == null) {
0639: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
0640: throw new TclVarException(interp, part1,
0641: part2, msg, missingName);
0642: }
0643: return null;
0644: }
0645: var = new Var();
0646: varNs.varTable.put(tail, var);
0647:
0648: // There is no hPtr member in Jacl, The hPtr combines the table
0649: // and the key used in a table lookup.
0650: var.hashKey = tail;
0651: var.table = varNs.varTable;
0652:
0653: var.ns = varNs;
0654: } else { // var wasn't found and not to create it
0655: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
0656: throw new TclVarException(interp, part1, part2,
0657: msg, noSuchVar);
0658: }
0659: return null;
0660: }
0661: }
0662: } else { // local var: look in frame varFrame
0663:
0664: if (varFrame.compiledLocals != null) { // look in compiled local array
0665: // Compiled local variable lookups would not
0666: // normally be done in compiled code via
0667: // lookupVar(). This lookup code would be
0668: // executed when a runtime get or set
0669: // operation is executed. A runtime get or
0670: // set operation could try to create a
0671: // var with the same name as a compiled local
0672: // var, so it would need to be created in the
0673: // compiled local array and not in the local
0674: // var hash table.
0675:
0676: Var[] compiledLocals = varFrame.compiledLocals;
0677: String[] compiledLocalsNames = varFrame.compiledLocalsNames;
0678: final int MAX = compiledLocals.length;
0679:
0680: for (int i = 0; i < MAX; i++) {
0681: if (compiledLocalsNames[i].equals(part1)) {
0682: Var clocal = compiledLocals[i];
0683: if (clocal == null) {
0684: // No compiled local with this name, init it.
0685: if (createPart1) {
0686: var = new Var();
0687: var.hashKey = part1;
0688: var.clearVarInHashtable();
0689:
0690: compiledLocals[i] = var;
0691: }
0692: } else {
0693: // Found existing compiled local var, make
0694: // sure it is a not a scoped non-local.
0695:
0696: if (clocal.isVarNonLocal()) {
0697: throw new TclRuntimeError(
0698: "can't lookup scoped variable \""
0699: + part1
0700: + "\" in local table");
0701: }
0702: var = clocal;
0703: }
0704: break;
0705: }
0706: }
0707: }
0708:
0709: if (var == null) { // look in the frame's var hash table
0710: table = varFrame.varTable;
0711: if (createPart1) {
0712: if (table == null) {
0713: table = new HashMap();
0714: varFrame.varTable = table;
0715: }
0716: var = (Var) table.get(part1);
0717: if (var == null) { // we are adding a new entry
0718: var = new Var();
0719: table.put(part1, var);
0720:
0721: // There is no hPtr member in Jacl, The hPtr combines
0722: // the table and the key used in a table lookup.
0723: var.hashKey = part1;
0724: var.table = table;
0725: }
0726: } else {
0727: if (table != null) {
0728: var = (Var) table.get(part1);
0729: }
0730: if (var == null) {
0731: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
0732: throw new TclVarException(interp, part1,
0733: part2, msg, noSuchVar);
0734: }
0735: return null;
0736: }
0737: }
0738: }
0739: }
0740:
0741: // If var is a link variable, we have a reference to some variable
0742: // that was created through an "upvar" or "global" command. Traverse
0743: // through any links until we find the referenced variable.
0744:
0745: while (var.isVarLink()) {
0746: var = var.linkto;
0747: }
0748:
0749: // If we're not dealing with an array element, return var.
0750:
0751: if (elName == null) {
0752: Var[] ret = interp.lookupVarResult;
0753: ret[0] = var;
0754: ret[1] = null;
0755: return ret;
0756: }
0757:
0758: return Var.lookupArrayElement(interp, part1, elName, flags,
0759: msg, createPart1, createPart2, var);
0760: }
0761:
0762: /* Given a ref to an array Var object, lookup the element
0763: * in the array indicated by "part2". */
0764:
0765: static Var[] lookupArrayElement(Interp interp, // Interpreter to use for lookup.
0766: String part1, // The name of an array, can't include elem.
0767: String part2, // Name of element within array, can't be null.
0768: int flags, // Only TCL.GLOBAL_ONLY, TCL.NAMESPACE_ONLY,
0769: // and TCL.LEAVE_ERR_MSG bits matter.
0770: String msg, // Verb to use in error messages, e.g.
0771: // "read" or "set". Only needed if
0772: // TCL.LEAVE_ERR_MSG is set in flags.
0773: boolean createPart1, // If true, create hash table entry for part 1
0774: // of name, if it doesn't already exist. If
0775: // false, return error if it doesn't exist.
0776: boolean createPart2, // If true, create hash table entry for part 2
0777: // of name, if it doesn't already exist. If
0778: // false, throw exception if it doesn't exist.
0779: Var var // Resolved ref to the array variable.
0780: ) throws TclException {
0781: // We're dealing with an array element. Make sure the variable is an
0782: // array and look up the element (create the element if desired).
0783:
0784: if (var.isVarUndefined() && !var.isVarArrayElement()) {
0785: if (!createPart1) {
0786: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
0787: throw new TclVarException(interp, part1, part2,
0788: msg, noSuchVar);
0789: }
0790: return null;
0791: }
0792:
0793: // Make sure we are not resurrecting a namespace variable from a
0794: // deleted namespace!
0795:
0796: if (((var.flags & IN_HASHTABLE) != 0)
0797: && (var.table == null)) {
0798: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
0799: throw new TclVarException(interp, part1, part2,
0800: msg, danglingVar);
0801: }
0802: return null;
0803: }
0804:
0805: var.setVarArray();
0806: var.clearVarUndefined();
0807: var.arraymap = new HashMap();
0808: } else if (!var.isVarArray()) {
0809: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
0810: throw new TclVarException(interp, part1, part2, msg,
0811: needArray);
0812: }
0813: return null;
0814: }
0815:
0816: Var arrayVar = var;
0817: HashMap arrayTable = var.arraymap;
0818: if (createPart2) {
0819: Var searchvar = (Var) arrayTable.get(part2);
0820:
0821: if (searchvar == null) { // new entry
0822: if (var.sidVec != null) {
0823: deleteSearches(var);
0824: }
0825:
0826: var = new Var();
0827: arrayTable.put(part2, var);
0828:
0829: // There is no hPtr member in Jacl, The hPtr combines the table
0830: // and the key used in a table lookup.
0831: var.hashKey = part2;
0832: var.table = arrayTable;
0833:
0834: var.ns = arrayVar.ns; // Will be null for local vars
0835: var.setVarArrayElement();
0836: } else {
0837: var = searchvar;
0838: }
0839: } else {
0840: var = (Var) arrayTable.get(part2);
0841: if (var == null) {
0842: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
0843: throw new TclVarException(interp, part1, part2,
0844: msg, noSuchElement);
0845: }
0846: return null;
0847: }
0848: }
0849:
0850: Var[] ret = interp.lookupVarResult;
0851: ret[0] = var; // The Var in the array
0852: ret[1] = arrayVar; // The array Var
0853: return ret;
0854: }
0855:
0856: /**
0857: * Tcl_GetVar2Ex -> getVar
0858: *
0859: * Query the value of a variable, given a two-part name consisting
0860: * of array name and element within array.
0861: *
0862: * @param interp the interp that holds the variable
0863: * @param part1 1st part of the variable name.
0864: * @param part2 2nd part of the variable name.
0865: * @param flags misc flags that control the actions of this method.
0866: * @return the value of the variable.
0867: */
0868:
0869: static TclObject getVar(Interp interp, // interpreter to look for the var in
0870: String part1, // Name of an array (if part2 is non-null)
0871: // or the name of a variable.
0872: String part2, // If non-null, gives the name of an element
0873: // in the array part1.
0874: int flags // OR-ed combination of TCL.GLOBAL_ONLY,
0875: // and TCL.LEAVE_ERR_MSG bits.
0876: ) throws TclException {
0877: Var[] result = lookupVar(interp, part1, part2, flags, "read",
0878: false, true);
0879:
0880: if (result == null) {
0881: // lookupVar() returns null only if TCL.LEAVE_ERR_MSG is
0882: // not part of the flags argument, return null in this case.
0883:
0884: return null;
0885: }
0886:
0887: return getVarPtr(interp, result[0], result[1], part1, part2,
0888: flags);
0889: }
0890:
0891: /**
0892: * TclPtrGetVar -> getVarPtr
0893: *
0894: * Query the value of a variable, given refs to the variables
0895: * Var objects.
0896: *
0897: * @param interp the interp that holds the variable
0898: * @param part1 1st part of the variable name.
0899: * @param part2 2nd part of the variable name.
0900: * @param flags misc flags that control the actions of this method.
0901: * @return the value of the variable.
0902: */
0903:
0904: static TclObject getVarPtr(Interp interp, // interpreter to look for the var in
0905: Var var, Var array, String part1, // Name of an array (if part2 is non-null)
0906: // or the name of a variable.
0907: String part2, // If non-null, gives the name of an element
0908: // in the array part1.
0909: int flags // OR-ed combination of TCL.GLOBAL_ONLY,
0910: // and TCL.LEAVE_ERR_MSG bits.
0911: ) throws TclException {
0912: try {
0913: // Invoke any traces that have been set for the variable.
0914:
0915: if ((var.traces != null)
0916: || ((array != null) && (array.traces != null))) {
0917: String msg = callTraces(
0918: interp,
0919: array,
0920: var,
0921: part1,
0922: part2,
0923: (flags & (TCL.NAMESPACE_ONLY | TCL.GLOBAL_ONLY))
0924: | TCL.TRACE_READS);
0925: if (msg != null) {
0926: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
0927: throw new TclVarException(interp, part1, part2,
0928: "read", msg);
0929: }
0930: return null;
0931: }
0932: }
0933:
0934: if (var.isVarScalar() && !var.isVarUndefined()) {
0935: return var.tobj;
0936: }
0937:
0938: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
0939: String msg;
0940: if (var.isVarUndefined() && (array != null)
0941: && !array.isVarUndefined()) {
0942: msg = noSuchElement;
0943: } else if (var.isVarArray()) {
0944: msg = isArray;
0945: } else {
0946: msg = noSuchVar;
0947: }
0948: throw new TclVarException(interp, part1, part2, "read",
0949: msg);
0950: }
0951: } finally {
0952: // If the variable doesn't exist anymore and no-one's using it,
0953: // then free up the relevant structures and hash table entries.
0954:
0955: if (var.isVarUndefined()) {
0956: cleanupVar(var, array);
0957: }
0958: }
0959:
0960: return null;
0961: }
0962:
0963: /**
0964: * Tcl_SetVar2Ex -> setVar
0965: *
0966: * Given a two-part variable name, which may refer either to a scalar
0967: * variable or an element of an array, change the value of the variable
0968: * to a new Tcl object value. See the setVarPtr() method for the
0969: * arguments to be passed to this method.
0970: */
0971:
0972: static TclObject setVar(Interp interp, // interp to search for the var in
0973: String part1, // Name of an array (if part2 is non-null)
0974: // or the name of a variable.
0975: String part2, // If non-null, gives the name of an element
0976: // in the array part1.
0977: TclObject newValue, // New value for variable.
0978: int flags // Various flags that tell how to set value:
0979: // any of TCL.GLOBAL_ONLY,
0980: // TCL.NAMESPACE_ONLY, TCL.APPEND_VALUE,
0981: // TCL.LIST_ELEMENT or TCL.LEAVE_ERR_MSG.
0982: ) throws TclException {
0983: Var[] result = lookupVar(interp, part1, part2, flags, "set",
0984: true, true);
0985: if (result == null) {
0986: return null;
0987: }
0988:
0989: return setVarPtr(interp, result[0], result[1], part1, part2,
0990: newValue, flags);
0991: }
0992:
0993: /**
0994: * TclPtrSetVar -> setVarPtr
0995: *
0996: * This method implements setting of a variable value that has
0997: * already been resolved into Var refrences. Pass the resolved
0998: * var refrences and a two-part variable name, which may refer
0999: * either to a scalar or an element of an array. This method will
1000: * change the value of the variable to a new TclObject value.
1001: * If the named scalar or array or element
1002: * doesn't exist then this method will create one.
1003: *
1004: * @param interp the interp that holds the variable
1005: * @param var a resolved Var ref
1006: * @param array a resolved Var ref
1007: * @param part1 1st part of the variable name.
1008: * @param part2 2nd part of the variable name.
1009: * @param newValue the new value for the variable
1010: * @param flags misc flags that control the actions of this method
1011: *
1012: * Returns a pointer to the TclObject holding the new value of the
1013: * variable. If the write operation was disallowed because an array was
1014: * expected but not found (or vice versa), then null is returned; if
1015: * the TCL.LEAVE_ERR_MSG flag is set, then an exception will be raised.
1016: * Note that the returned object may not be the same one referenced
1017: * by newValue because variable traces may modify the variable's value.
1018: * The value of the given variable is set. If either the array or the
1019: * entry didn't exist then a new variable is created.
1020: *
1021: * The reference count is decremented for any old value of the variable
1022: * and incremented for its new value. If the new value for the variable
1023: * is not the same one referenced by newValue (perhaps as a result
1024: * of a variable trace), then newValue's ref count is left unchanged
1025: * by Tcl_SetVar2Ex. newValue's ref count is also left unchanged if
1026: * we are appending it as a string value: that is, if "flags" includes
1027: * TCL.APPEND_VALUE but not TCL.LIST_ELEMENT.
1028: *
1029: * The reference count for the returned object is _not_ incremented: if
1030: * you want to keep a reference to the object you must increment its
1031: * ref count yourself.
1032: */
1033:
1034: static TclObject setVarPtr(Interp interp, // interp to search for the var in
1035: Var var, Var array, String part1, // Name of an array (if part2 is non-null)
1036: // or the name of a variable.
1037: String part2, // If non-null, gives the name of an element
1038: // in the array part1.
1039: TclObject newValue, // New value for variable.
1040: int flags // Various flags that tell how to set value:
1041: // any of TCL.GLOBAL_ONLY,
1042: // TCL.NAMESPACE_ONLY, TCL.APPEND_VALUE,
1043: // TCL.LIST_ELEMENT or TCL.LEAVE_ERR_MSG.
1044: ) throws TclException {
1045: TclObject oldValue;
1046: String bytes;
1047:
1048: // If the variable is in a hashtable and its table field is null, then we
1049: // may have an upvar to an array element where the array was deleted
1050: // or an upvar to a namespace variable whose namespace was deleted.
1051: // Generate an error (allowing the variable to be reset would screw up
1052: // our storage allocation and is meaningless anyway).
1053:
1054: if (((var.flags & IN_HASHTABLE) != 0) && (var.table == null)) {
1055: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
1056: if (var.isVarArrayElement()) {
1057: throw new TclVarException(interp, part1, part2,
1058: "set", danglingElement);
1059: } else {
1060: throw new TclVarException(interp, part1, part2,
1061: "set", danglingVar);
1062: }
1063: }
1064: return null;
1065: }
1066:
1067: // It's an error to try to set an array variable itself.
1068:
1069: if (var.isVarArray() && !var.isVarUndefined()) {
1070: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
1071: throw new TclVarException(interp, part1, part2, "set",
1072: isArray);
1073: }
1074: return null;
1075: }
1076:
1077: // At this point, if we were appending, we used to call read traces: we
1078: // treated append as a read-modify-write. However, it seemed unlikely to
1079: // us that a real program would be interested in such reads being done
1080: // during a set operation.
1081:
1082: // Set the variable's new value. If appending, append the new value to
1083: // the variable, either as a list element or as a string. Also, if
1084: // appending, then if the variable's old value is unshared we can modify
1085: // it directly, otherwise we must create a new copy to modify: this is
1086: // "copy on write".
1087:
1088: try {
1089: oldValue = var.tobj;
1090:
1091: if ((flags & TCL.APPEND_VALUE) != 0) {
1092: if (var.isVarUndefined() && (oldValue != null)) {
1093: oldValue.release(); // discard old value
1094: var.tobj = null;
1095: oldValue = null;
1096: }
1097: if ((flags & TCL.LIST_ELEMENT) != 0) { // append list element
1098: if (oldValue == null) {
1099: oldValue = TclList.newInstance();
1100: var.tobj = oldValue;
1101: oldValue.preserve(); // since var is referenced
1102: } else if (oldValue.isShared()) { // append to copy
1103: var.tobj = oldValue.duplicate();
1104: oldValue.release();
1105: oldValue = var.tobj;
1106: oldValue.preserve(); // since var is referenced
1107: }
1108: TclList.append(interp, oldValue, newValue);
1109: } else { // append string
1110: // We append newValue's bytes but don't change its ref count.
1111:
1112: bytes = newValue.toString();
1113: if (oldValue == null) {
1114: var.tobj = TclString.newInstance(bytes);
1115: var.tobj.preserve();
1116: } else {
1117: if (oldValue.isShared()) { // append to copy
1118: var.tobj = oldValue.duplicate();
1119: oldValue.release();
1120: oldValue = var.tobj;
1121: oldValue.preserve(); // since var is referenced
1122: }
1123: TclString.append(oldValue, bytes);
1124: }
1125: }
1126: } else {
1127: if ((flags & TCL.LIST_ELEMENT) != 0) { // set var to list element
1128: int listFlags;
1129:
1130: // We set the variable to the result of converting newValue's
1131: // string rep to a list element. We do not change newValue's
1132: // ref count.
1133:
1134: if (oldValue != null) {
1135: oldValue.release(); // discard old value
1136: }
1137: bytes = newValue.toString();
1138: listFlags = Util.scanElement(interp, bytes);
1139: StringBuffer sb = new StringBuffer(64);
1140: Util.convertElement(bytes, listFlags, sb);
1141: oldValue = TclString.newInstance(sb.toString());
1142: var.tobj = oldValue;
1143: var.tobj.preserve();
1144: } else if (newValue != oldValue) {
1145: var.tobj = newValue;
1146: newValue.preserve(); // var is another ref
1147: if (oldValue != null) {
1148: oldValue.release(); // discard old value
1149: }
1150: }
1151: }
1152: var.setVarScalar();
1153: var.clearVarUndefined();
1154: if (array != null) {
1155: array.clearVarUndefined();
1156: }
1157:
1158: // Invoke any write traces for the variable.
1159:
1160: if ((var.traces != null)
1161: || ((array != null) && (array.traces != null))) {
1162:
1163: String msg = callTraces(
1164: interp,
1165: array,
1166: var,
1167: part1,
1168: part2,
1169: (flags & (TCL.GLOBAL_ONLY | TCL.NAMESPACE_ONLY))
1170: | TCL.TRACE_WRITES);
1171: if (msg != null) {
1172: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
1173: throw new TclVarException(interp, part1, part2,
1174: "set", msg);
1175: }
1176: return null; // Same as "goto cleanup" in C verison
1177: }
1178: }
1179:
1180: // Return the variable's value unless the variable was changed in some
1181: // gross way by a trace (e.g. it was unset and then recreated as an
1182: // array).
1183:
1184: if (var.isVarScalar() && !var.isVarUndefined()) {
1185: return var.tobj;
1186: }
1187:
1188: // A trace changed the value in some gross way. Return an empty string
1189: // object.
1190:
1191: return TclString.newInstance("");
1192: } finally {
1193: // If the variable doesn't exist anymore and no-one's using it,
1194: // then free up the relevant structures and hash table entries.
1195:
1196: if (var.isVarUndefined()) {
1197: cleanupVar(var, array);
1198: }
1199: }
1200: }
1201:
1202: // This method is invoked to initialize a new compiled
1203: // local scalar variable when the compiled local slot
1204: // is null. Initializing a compiled local scalar is
1205: // a very common operation, so it is highly optimized here.
1206:
1207: static TclObject initVarCompiledLocalScalar(final Interp interp, // interp to search for the var in
1208: final String varname, // Name of scalar variable.
1209: final TclObject newValue, // New value for scalar variable.
1210: final Var[] compiledLocals, // compiled local array
1211: final int localIndex) // index into compiled local array, 0 to N.
1212: throws TclException {
1213: // Extra checking
1214: final boolean validate = false;
1215:
1216: if (validate) {
1217: CallFrame varFrame = interp.varFrame;
1218:
1219: if (varFrame == null) {
1220: throw new TclRuntimeError("null interp.varFrame");
1221: }
1222: if (varFrame != interp.frame) {
1223: throw new TclRuntimeError(
1224: "interp.frame vs interp.varFrame mismatch");
1225: }
1226: if (varFrame.isProcCallFrame == false) {
1227: throw new TclRuntimeError(
1228: "expected isProcCallFrame to be true");
1229: }
1230: if (varFrame.compiledLocals == null) {
1231: throw new TclRuntimeError(
1232: "expected non-null compiledLocals");
1233: }
1234:
1235: // Double check that scalar name is not actually an array
1236: // name like "arr(foo)".
1237:
1238: if (Var.isArrayVarname(varname)) {
1239: throw new TclRuntimeError(
1240: "unexpected array variable name \"" + varname
1241: + "\"");
1242: }
1243:
1244: // Look in local table, there should not be an entry
1245: HashMap table = varFrame.varTable;
1246:
1247: if (table != null && table.size() > 0) {
1248: Var var = (Var) table.get(varname);
1249: if (var != null) {
1250: throw new TclException(interp,
1251: "duplicate var found in local table for "
1252: + varname);
1253: }
1254: }
1255:
1256: if (compiledLocals[localIndex] != null) {
1257: throw new TclException(interp,
1258: "compiled local slot should be null for "
1259: + varname);
1260: }
1261:
1262: // A compiled local that is a scoped value would never
1263: // be initialized by this method.
1264:
1265: if (varname.indexOf("::") != -1) {
1266: throw new TclRuntimeError(
1267: "scoped scalar should neve be initialized here "
1268: + varname);
1269: }
1270:
1271: } // end if (validate) block
1272:
1273: // At this point, we know that a var with the
1274: // same name can't exist in the local table.
1275: // We also know that the compiled local slot
1276: // is null, so the var can't exist in an
1277: // undefined state. There are no funky state
1278: // issues like a var with traces set or
1279: // a var in another frame linked to this one.
1280: // The varname will always be a simple scalar.
1281:
1282: Var var = new Var();
1283: if (validate) {
1284: // Double check Var init state assumptions.
1285: if (var.flags != (SCALAR | UNDEFINED | IN_HASHTABLE)) {
1286: throw new TclRuntimeError("invalid Var flags state");
1287: }
1288: if (var.tobj != null) {
1289: throw new TclRuntimeError(
1290: "expected null Var tobj value");
1291: }
1292: if (var.arraymap != null) {
1293: throw new TclRuntimeError(
1294: "expected null Var arraymap value");
1295: }
1296: if (var.linkto != null) {
1297: throw new TclRuntimeError(
1298: "expected null Var linkto value");
1299: }
1300: if (var.table != null) {
1301: throw new TclRuntimeError("expected null Var table");
1302: }
1303: }
1304:
1305: // Inline Var init and setVarPtr() logic that applies to
1306: // scalar variables.
1307:
1308: //var.setVarScalar();
1309: //var.clearVarInHashtable();
1310: //var.clearVarUndefined();
1311: var.flags = SCALAR;
1312:
1313: var.hashKey = varname;
1314:
1315: // Assign TclObject value for scalar and incr ref count
1316:
1317: var.tobj = newValue;
1318: newValue.preserve();
1319:
1320: // Add var to the compiled local array.
1321:
1322: compiledLocals[localIndex] = var;
1323:
1324: return newValue;
1325: }
1326:
1327: // This method is invoked to set a compiled local scalar
1328: // variable when the resolved var is invalid. It will
1329: // never be invoked when the compiled local slot is null.
1330: // This method is not in the critical execution path.
1331:
1332: static TclObject setVarCompiledLocalScalarInvalid(Interp interp, // interp to search for the var in
1333: String varname, // Name of scalar variable.
1334: TclObject newValue) // New value for scalar variable.
1335: throws TclException {
1336: // Extra checking
1337: final boolean validate = false;
1338:
1339: if (validate) {
1340: // Lookup current variable frame on the stack. This method
1341: // is only even invoked after a CallFrame with a compiled
1342: // local array has already been pushed onto the stack.
1343:
1344: CallFrame varFrame = interp.varFrame;
1345:
1346: if (varFrame == null) {
1347: throw new TclRuntimeError("null interp.varFrame");
1348: }
1349: if (varFrame != interp.frame) {
1350: throw new TclRuntimeError(
1351: "interp.frame vs interp.varFrame mismatch");
1352: }
1353: if (varFrame.isProcCallFrame == false) {
1354: throw new TclRuntimeError(
1355: "expected isProcCallFrame to be true");
1356: }
1357:
1358: // Double check that scalar name is not actually an array
1359: // name like "arr(foo)".
1360:
1361: if (Var.isArrayVarname(varname)) {
1362: throw new TclRuntimeError(
1363: "unexpected array variable name \"" + varname
1364: + "\"");
1365: }
1366:
1367: // A scoped var name should always be initialized
1368: // as a link var. A non-global scoped link var
1369: // should never be pass in here.
1370:
1371: if (!varname.startsWith("::")
1372: && (-1 != varname.indexOf("::"))) {
1373: throw new TclRuntimeError("unexpected scoped scalar");
1374: }
1375:
1376: } // end if (validate) block
1377:
1378: // This method would never be invoked with a null
1379: // compiled locals slot. It could be invoked when
1380: // the compiled local is unset, has traces,
1381: // or when the link var is unset or has traces.
1382:
1383: return setVar(interp, varname, null, newValue,
1384: TCL.LEAVE_ERR_MSG);
1385: }
1386:
1387: // This method is invoked to get the value of a
1388: // scalar variable that does not have a valid
1389: // resolved ref. This method could be invoked
1390: // when traces are set on a compiled local,
1391: // for example. This method is only
1392: // ever invoked from a compiled proc
1393: // implementation. This method is not in the
1394: // critical execution path.
1395:
1396: static TclObject getVarCompiledLocalScalarInvalid(Interp interp, // interp to search for the var in
1397: String varname) // Name of scalar variable.
1398: throws TclException {
1399: // Extra checking
1400: final boolean validate = false;
1401:
1402: if (validate) {
1403: CallFrame varFrame = interp.varFrame;
1404:
1405: if (varFrame == null) {
1406: throw new TclRuntimeError("null interp.varFrame");
1407: }
1408: if (varFrame != interp.frame) {
1409: throw new TclRuntimeError(
1410: "interp.frame vs interp.varFrame mismatch");
1411: }
1412:
1413: if (varFrame.isProcCallFrame == false) {
1414: throw new TclRuntimeError(
1415: "expected isProcCallFrame to be true");
1416: }
1417: if (varFrame.compiledLocals == null) {
1418: throw new TclRuntimeError(
1419: "expected non-null compiledLocals");
1420: }
1421:
1422: if (varname == null) {
1423: throw new TclRuntimeError("varname can't be null");
1424: }
1425:
1426: // Double check that varname is not actually an array
1427: // name like "arr(foo)".
1428:
1429: if ((varname.charAt(varname.length() - 1) == ')')
1430: && (varname.indexOf('(') != -1)) {
1431: throw new TclRuntimeError(
1432: "unexpected array variable name \"" + varname
1433: + "\"");
1434: }
1435:
1436: // A non-global scoped link var should never be passed in here.
1437:
1438: if (!varname.startsWith("::")
1439: && (-1 != varname.indexOf("::"))) {
1440: throw new TclRuntimeError("unexpected scoped scalar");
1441: }
1442: }
1443:
1444: return Var.getVar(interp, varname, null, TCL.LEAVE_ERR_MSG);
1445: }
1446:
1447: // This method is invoked to initialize a new compiled
1448: // local array variable when the compiled local slot
1449: // is null. Initializing a compiled local array is
1450: // a very common operation, so it is optimized.
1451:
1452: static TclObject initVarCompiledLocalArray(final Interp interp, // interp to search for the var in
1453: final String varname, // Name of scalar variable.
1454: final String key, // Array key, can't be null
1455: final TclObject newValue, // New value for array entry variable.
1456: final Var[] compiledLocals, // compiled local array
1457: final int localIndex) // index into compiled local array, 0 to N.
1458: throws TclException {
1459: // Extra checking
1460: final boolean validate = false;
1461:
1462: if (validate) {
1463: // Lookup current variable frame on the stack. This method
1464: // is only even invoked after a CallFrame with a compiled
1465: // local array has already been pushed onto the stack.
1466:
1467: CallFrame varFrame = interp.varFrame;
1468:
1469: if (varFrame == null) {
1470: throw new TclRuntimeError("null interp.varFrame");
1471: }
1472: if (varFrame != interp.frame) {
1473: throw new TclRuntimeError(
1474: "interp.frame vs interp.varFrame mismatch");
1475: }
1476: if (varFrame.isProcCallFrame == false) {
1477: throw new TclRuntimeError(
1478: "expected isProcCallFrame to be true");
1479: }
1480: if (varFrame.compiledLocals == null) {
1481: throw new TclRuntimeError(
1482: "expected non-null compiledLocals");
1483: }
1484:
1485: // Double check that varname is not actually an array
1486: // name like "arr(foo)". A compiled array name should be
1487: // seperated into two elements.
1488:
1489: if (Var.isArrayVarname(varname)) {
1490: throw new TclRuntimeError(
1491: "unexpected array variable name \"" + varname
1492: + "\"");
1493: }
1494:
1495: if (key == null) {
1496: throw new TclRuntimeError("null array key");
1497: }
1498:
1499: // Look in local table, there should not be an entry for this varname
1500: HashMap table = varFrame.varTable;
1501:
1502: if (table != null && table.size() > 0) {
1503: Var var = (Var) table.get(varname);
1504: if (var != null) {
1505: throw new TclException(interp,
1506: "duplicate var found in local table for "
1507: + varname);
1508: }
1509: }
1510:
1511: if (compiledLocals[localIndex] != null) {
1512: throw new TclException(interp,
1513: "compiled local slot should be null for "
1514: + varname);
1515: }
1516:
1517: // A compiled local that is a scoped value would never
1518: // be initialized by this method.
1519:
1520: if (varname.indexOf("::") != -1) {
1521: throw new TclRuntimeError(
1522: "scoped scalar should neve be initialized here "
1523: + varname);
1524: }
1525:
1526: } // end if (validate)
1527:
1528: // At this point, we know that a var with the
1529: // same name can't exist in the local table.
1530: // We also know that the compiled local slot
1531: // is null, so the var can't exist in an
1532: // undefined state. There are no funky state
1533: // issues like a var with traces set or
1534: // a var in another frame linked to this one.
1535: // The varname will always be an unlinked array var.
1536:
1537: Var var = new Var();
1538: var.clearVarInHashtable();
1539: var.hashKey = varname;
1540:
1541: // Add var to the compiled local array
1542: // so that it will be found and used
1543: // by setVar().
1544:
1545: compiledLocals[localIndex] = var;
1546:
1547: // Set the variable, can't use setVarPtr()
1548: // since lookupVar() does the array element
1549: // create logic.
1550:
1551: return setVar(interp, varname, key, newValue, TCL.LEAVE_ERR_MSG);
1552: }
1553:
1554: // This method is invoked to set a compiled local array
1555: // variable when the resolved var is invalid. It will
1556: // never be invoked when the compiled local slot is null.
1557: // This method is not in the critical execution path.
1558:
1559: static TclObject setVarCompiledLocalArrayInvalid(Interp interp, // interp to search for the var in
1560: String varname, // Name of scalar variable.
1561: String key, // Array key, can't be null
1562: TclObject newValue) // New value for scalar variable.
1563: throws TclException {
1564: // Extra checking
1565: final boolean validate = false;
1566:
1567: if (validate) {
1568: // Lookup current variable frame on the stack. This method
1569: // is only even invoked after a CallFrame with a compiled
1570: // local array has already been pushed onto the stack.
1571:
1572: CallFrame varFrame = interp.varFrame;
1573:
1574: if (varFrame == null) {
1575: throw new TclRuntimeError("null interp.varFrame");
1576: }
1577: if (varFrame != interp.frame) {
1578: throw new TclRuntimeError(
1579: "interp.frame vs interp.varFrame mismatch");
1580: }
1581: if (varFrame.isProcCallFrame == false) {
1582: throw new TclRuntimeError(
1583: "expected isProcCallFrame to be true");
1584: }
1585: if (varFrame.compiledLocals == null) {
1586: throw new TclRuntimeError(
1587: "expected non-null compiledLocals");
1588: }
1589:
1590: // Double check that varname is not actually an array
1591: // name like "arr(foo)".
1592:
1593: if (Var.isArrayVarname(varname)) {
1594: throw new TclRuntimeError(
1595: "unexpected array variable name \"" + varname
1596: + "\"");
1597: }
1598:
1599: if (key == null) {
1600: throw new TclRuntimeError("null array key");
1601: }
1602:
1603: // Look in local table, there should not be an entry for this varname
1604: HashMap table = varFrame.varTable;
1605:
1606: if (table != null && table.size() > 0) {
1607: Var var = (Var) table.get(varname);
1608: if (var != null) {
1609: throw new TclException(interp,
1610: "duplicate var found in local table for "
1611: + varname);
1612: }
1613: }
1614:
1615: // A compiled local that is a scoped value would never
1616: // be initialized by this method.
1617:
1618: if (varname.indexOf("::") != -1) {
1619: throw new TclRuntimeError(
1620: "scoped scalar should neve be initialized here "
1621: + varname);
1622: }
1623:
1624: } // end if (validate)
1625:
1626: // The compiled local array var already exists, but it
1627: // could not be resolved to a valid var. This can happen
1628: // when an undefined variable linked into another scope is
1629: // being defined for the first time. This can also
1630: // happen when the compiled local is not an array.
1631: // Handle these cases by invoking setVar() to either
1632: // raise the proper error or set the var.
1633:
1634: return setVar(interp, varname, key, newValue, TCL.LEAVE_ERR_MSG);
1635: }
1636:
1637: // This method is invoked to get the value of an
1638: // array variable that is associated with a compiled
1639: // local slot. This method is invoked when the
1640: // compiled local slot is null or when the var
1641: // can't be resolved to a valid array variable.
1642: // This method will only attempt to read a variable.
1643: // It will not init a compiled local slot.
1644: // This method is only ever invoked from a compiled
1645: // proc implementation. This method is not in the
1646: // critical execution path.
1647:
1648: static TclObject getVarCompiledLocalArrayInvalid(Interp interp, // interp to search for the var in
1649: String varname, // Name of array variable.
1650: String key) // array key, can't be null
1651: throws TclException {
1652: // Extra checking
1653: final boolean validate = false;
1654:
1655: if (validate) {
1656: CallFrame varFrame = interp.varFrame;
1657:
1658: if (varFrame == null) {
1659: throw new TclRuntimeError("null interp.varFrame");
1660: }
1661: if (varFrame != interp.frame) {
1662: throw new TclRuntimeError(
1663: "interp.frame vs interp.varFrame mismatch");
1664: }
1665: if (varFrame.compiledLocals == null) {
1666: throw new TclRuntimeError(
1667: "expected non-null compiledLocals");
1668: }
1669:
1670: if (key == null) {
1671: throw new TclRuntimeError("array key can't be null");
1672: }
1673:
1674: // Double check that varname is not actually an array
1675: // name like "arr(foo)".
1676:
1677: if ((varname.charAt(varname.length() - 1) == ')')
1678: && (varname.indexOf('(') != -1)) {
1679: throw new TclRuntimeError(
1680: "unexpected array variable name \"" + varname
1681: + "\"");
1682: }
1683:
1684: } // end if (validate) block
1685:
1686: return Var.getVar(interp, varname, key, TCL.LEAVE_ERR_MSG);
1687: }
1688:
1689: // This method is invoked to get the value of an
1690: // array variable that is associated with a compiled
1691: // local slot. This method is invoked when the
1692: // compiled local slot contains a valid resolved ref.
1693: // This method is only ever invoked from a compiled
1694: // proc implementation. This method is in the
1695: // critical execution path.
1696:
1697: static TclObject getVarCompiledLocalArray(final Interp interp,
1698: final String varname, // name of array variable
1699: final String key, // array key, can't be null
1700: final Var resolved, // resolved array variable
1701: final boolean leaveErrMsg) // If true, will raise a
1702: // TclException when the array
1703: // element does not exist. If
1704: // false, then return null.
1705: throws TclException {
1706: int flags = 0;
1707: if (leaveErrMsg) {
1708: // If leaveErrMsg is true, will raise a TclException
1709: // in lookupArrayElement() if the array element does
1710: // not exist or is an undefined var. If leaveErrMsg is
1711: // false, then return null so that the calling code
1712: // can handle an array variable that does not exist
1713: // without throwing and catching an exception.
1714:
1715: flags = TCL.LEAVE_ERR_MSG;
1716: }
1717:
1718: Var[] result = Var.lookupArrayElement(interp, varname, key,
1719: flags, "read", false, false, resolved);
1720: if (result == null) {
1721: return null;
1722: }
1723:
1724: // Always pass TCL.LEAVE_ERR_MSG so that an exception
1725: // will be raised in case a trace raises an exception.
1726:
1727: return Var.getVarPtr(interp, result[0], result[1], varname,
1728: key, TCL.LEAVE_ERR_MSG);
1729: }
1730:
1731: // This method is invoked to set the value of an
1732: // array variable that is associated with a compiled
1733: // local slot. This method is invoked when the
1734: // compiled local slot can be resolved to an array.
1735: // This method is only ever invoked from a compiled
1736: // proc implementation. This method is in the
1737: // critical execution path.
1738:
1739: static TclObject setVarCompiledLocalArray(final Interp interp,
1740: final String varname, // name of array variable
1741: final String key, // array key, can't be null
1742: final TclObject newValue, final Var resolved) // resolved array variable
1743: throws TclException {
1744: // Raise TclException instead of returning null
1745: final int flags = TCL.LEAVE_ERR_MSG;
1746:
1747: Var[] result = lookupArrayElement(interp, varname, key, flags,
1748: "set", false, true, resolved);
1749:
1750: return setVarPtr(interp, result[0], result[1], varname, key,
1751: newValue, flags);
1752: }
1753:
1754: /**
1755: * TclIncrVar2 -> incrVar
1756: *
1757: * Given a two-part variable name, which may refer either to a scalar
1758: * variable or an element of an array, increment the Tcl object value
1759: * of the variable by a specified amount.
1760: *
1761: * @param part1 1st part of the variable name.
1762: * @param part2 2nd part of the variable name.
1763: * @param incrAmount Amount to be added to variable.
1764: * @param flags misc flags that control the actions of this method
1765: *
1766: * Results:
1767: * Returns a reference to the TclObject holding the new value of the
1768: * variable. If the specified variable doesn't exist, or there is a
1769: * clash in array usage, or an error occurs while executing variable
1770: * traces, then a TclException will be raised.
1771: *
1772: * Side effects:
1773: * The value of the given variable is incremented by the specified
1774: * amount. If either the array or the entry didn't exist then a new
1775: * variable is created. The ref count for the returned object is _not_
1776: * incremented to reflect the returned reference; if you want to keep a
1777: * reference to the object you must increment its ref count yourself.
1778: *
1779: *----------------------------------------------------------------------
1780: */
1781:
1782: static TclObject incrVar(Interp interp, // Command interpreter in which variable is
1783: // to be found.
1784: String part1, // Reference to a string holding the name of
1785: // an array (if part2 is non-null) or the
1786: // name of a variable.
1787: String part2, // If non-null, reference to a string holding
1788: // the name of an element in the array
1789: // part1.
1790: int incrAmount, // Amount to be added to variable.
1791: int flags // Various flags that tell how to incr value:
1792: // any of TCL.GLOBAL_ONLY,
1793: // TCL.NAMESPACE_ONLY, TCL.APPEND_VALUE,
1794: // TCL.LIST_ELEMENT, TCL.LEAVE_ERR_MSG.
1795: ) throws TclException {
1796: TclObject varValue = null;
1797: boolean createdNewObj; // Set to true if var's value object is shared
1798: // so we must increment a copy (i.e. copy
1799: // on write).
1800: int i;
1801: boolean err;
1802:
1803: // There are two possible error conditions that depend on the setting of
1804: // TCL.LEAVE_ERR_MSG. an exception could be raised or null could be returned
1805: err = false;
1806: try {
1807: varValue = getVar(interp, part1, part2, flags);
1808: } catch (TclException e) {
1809: err = true;
1810: throw e;
1811: } finally {
1812: // FIXME : is this the correct way to catch the error?
1813: if (err || varValue == null) {
1814: interp
1815: .addErrorInfo("\n (reading value of variable to increment)");
1816: }
1817: }
1818:
1819: // Increment the variable's value. If the object is unshared we can
1820: // modify it directly, otherwise we must create a new copy to modify:
1821: // this is "copy on write". The incr() method will free the old
1822: // string rep since it is no longer valid.
1823:
1824: createdNewObj = false;
1825: if (varValue.isShared()) {
1826: varValue = varValue.duplicate();
1827: createdNewObj = true;
1828: }
1829:
1830: try {
1831: TclInteger.incr(interp, varValue, incrAmount);
1832: } catch (TclException e) {
1833: if (createdNewObj) {
1834: varValue.release(); // free unneeded copy
1835: }
1836: throw e;
1837: }
1838:
1839: // Store the variable's new value and run any write traces.
1840:
1841: return setVar(interp, part1, part2, varValue, flags);
1842: }
1843:
1844: /**
1845: * Tcl_UnsetVar2 -> unsetVar
1846: *
1847: * Unset a variable, given a two-part name consisting of array
1848: * name and element within array.
1849: *
1850: * @param part1 1st part of the variable name.
1851: * @param part2 2nd part of the variable name.
1852: * @param flags misc flags that control the actions of this method.
1853: *
1854: * If part1 and part2 indicate a local or global variable in interp,
1855: * it is deleted. If part1 is an array name and part2 is null, then
1856: * the whole array is deleted.
1857: *
1858: */
1859:
1860: static void unsetVar(Interp interp, // Command interpreter in which var is
1861: // to be looked up.
1862: String part1, // Name of variable or array.
1863: String part2, // Name of element within array or null.
1864: int flags // OR-ed combination of any of
1865: // TCL.GLOBAL_ONLY, TCL.NAMESPACE_ONLY,
1866: // TCL.LEAVE_ERR_MSG.
1867: ) throws TclException {
1868: Var dummyVar;
1869: Var var;
1870: Var array;
1871: //ActiveVarTrace active;
1872: TclObject obj;
1873: int result;
1874:
1875: // FIXME : what about the null return vs exception thing here?
1876: Var[] lookup_result = lookupVar(interp, part1, part2, flags,
1877: "unset", false, false);
1878: if (lookup_result == null) {
1879: throw new TclRuntimeError("unexpected null reference");
1880: }
1881:
1882: var = lookup_result[0];
1883: array = lookup_result[1];
1884:
1885: result = (var.isVarUndefined() ? TCL.ERROR : TCL.OK);
1886:
1887: if ((array != null) && (array.sidVec != null)) {
1888: deleteSearches(array);
1889: }
1890:
1891: // The code below is tricky, because of the possibility that
1892: // a trace procedure might try to access a variable being
1893: // deleted. To handle this situation gracefully, do things
1894: // in three steps:
1895: // 1. Copy the contents of the variable to a dummy variable
1896: // structure, and mark the original Var structure as undefined.
1897: // 2. Invoke traces and clean up the variable, using the dummy copy.
1898: // 3. If at the end of this the original variable is still
1899: // undefined and has no outstanding references, then delete
1900: // it (but it could have gotten recreated by a trace).
1901:
1902: dummyVar = new Var();
1903: //FIXME: Var class really should implement clone to make a bit copy.
1904: dummyVar.tobj = var.tobj;
1905: dummyVar.arraymap = var.arraymap;
1906: dummyVar.linkto = var.linkto;
1907: dummyVar.traces = var.traces;
1908: dummyVar.flags = var.flags;
1909: dummyVar.hashKey = var.hashKey;
1910: dummyVar.table = var.table;
1911: dummyVar.refCount = var.refCount;
1912: dummyVar.ns = var.ns;
1913:
1914: var.setVarUndefined();
1915: var.setVarScalar();
1916: var.tobj = null; // dummyVar points to any value object
1917: var.arraymap = null;
1918: var.linkto = null;
1919: var.traces = null;
1920: var.sidVec = null;
1921:
1922: // Call trace procedures for the variable being deleted. Then delete
1923: // its traces. Be sure to abort any other traces for the variable
1924: // that are still pending. Special tricks:
1925: // 1. We need to increment var's refCount around this: CallTraces
1926: // will use dummyVar so it won't increment var's refCount itself.
1927: // 2. Turn off the TRACE_ACTIVE flag in dummyVar: we want to
1928: // call unset traces even if other traces are pending.
1929:
1930: if ((dummyVar.traces != null)
1931: || ((array != null) && (array.traces != null))) {
1932: var.refCount++;
1933: dummyVar.flags &= ~TRACE_ACTIVE;
1934: callTraces(interp, array, dummyVar, part1, part2,
1935: (flags & (TCL.GLOBAL_ONLY | TCL.NAMESPACE_ONLY))
1936: | TCL.TRACE_UNSETS);
1937:
1938: dummyVar.traces = null;
1939:
1940: // Active trace stuff is not part of Jacl's interp
1941:
1942: var.refCount--;
1943: }
1944:
1945: // If the variable is an array, delete all of its elements. This must be
1946: // done after calling the traces on the array, above (that's the way
1947: // traces are defined). If it is a scalar, "discard" its object
1948: // (decrement the ref count of its object, if any).
1949:
1950: if (dummyVar.isVarArray() && !dummyVar.isVarUndefined()) {
1951: deleteArray(interp, part1, dummyVar,
1952: (flags & (TCL.GLOBAL_ONLY | TCL.NAMESPACE_ONLY))
1953: | TCL.TRACE_UNSETS);
1954: }
1955: if (dummyVar.isVarScalar() && (dummyVar.tobj != null)) {
1956: obj = dummyVar.tobj;
1957: obj.release();
1958: dummyVar.tobj = null;
1959: }
1960:
1961: // If the variable was a namespace variable, decrement its reference count.
1962:
1963: if ((var.flags & NAMESPACE_VAR) != 0) {
1964: var.flags &= ~NAMESPACE_VAR;
1965: var.refCount--;
1966: }
1967:
1968: // Finally, if the variable is truly not in use then free up its Var
1969: // structure and remove it from its hash table, if any. The ref count of
1970: // its value object, if any, was decremented above.
1971:
1972: cleanupVar(var, array);
1973:
1974: Var.setUndefinedToNull(interp, part1, part2);
1975:
1976: // It's an error to unset an undefined variable.
1977:
1978: if (result != TCL.OK) {
1979: if ((flags & TCL.LEAVE_ERR_MSG) != 0) {
1980: throw new TclVarException(interp, part1, part2,
1981: "unset", ((array == null) ? noSuchVar
1982: : noSuchElement));
1983: }
1984: }
1985: }
1986:
1987: /**
1988: * Tcl_TraceVar2 -> traceVar
1989: *
1990: * Trace a variable, given a two-part name consisting of array
1991: * name and element within array.
1992: *
1993: * @param part1 1st part of the variable name.
1994: * @param part2 2nd part of the variable name.
1995: * @param flags misc flags that control the actions of this method.
1996: * @param trace the trace to comand to add.
1997: */
1998:
1999: static void traceVar(Interp interp, // interp in which var is located
2000: String part1, // Name of scalar variable or array.
2001: String part2, // Name of element within array; null means
2002: // trace applies to scalar variable or array
2003: // as-a-whole.
2004: int flags, // OR-ed collection of bits, including any
2005: // of TCL.TRACE_READS, TCL.TRACE_WRITES,
2006: // TCL.TRACE_UNSETS, TCL.GLOBAL_ONLY,
2007: // and TCL.NAMESPACE_ONLY.
2008: VarTrace proc // Procedure to call when specified ops are
2009: // invoked upon var.
2010: ) throws TclException {
2011: Var[] result;
2012: Var var, array;
2013:
2014: // FIXME: what about the exception problem here?
2015: result = lookupVar(interp, part1, part2,
2016: (flags | TCL.LEAVE_ERR_MSG), "trace", true, true);
2017: if (result == null) {
2018: throw new TclException(interp, "");
2019: }
2020:
2021: var = result[0];
2022: array = result[1];
2023:
2024: // Set up trace information. Set a flag to indicate that traces
2025: // exists so that resolveScalar() can determine if traces
2026: // are set by checking only the Var flags filed. The rest of
2027: // the code in this module makes use of the var.traces field.
2028:
2029: if (var.traces == null) {
2030: var.setVarTraceExists();
2031: var.traces = new ArrayList();
2032: }
2033:
2034: TraceRecord rec = new TraceRecord();
2035: rec.trace = proc;
2036: rec.flags = flags
2037: & (TCL.TRACE_READS | TCL.TRACE_WRITES
2038: | TCL.TRACE_UNSETS | TCL.TRACE_ARRAY);
2039:
2040: var.traces.add(0, rec);
2041:
2042: // FIXME: is this needed ?? It was in Jacl but not 8.1
2043:
2044: /*
2045: // When inserting a trace for an array on an UNDEFINED variable,
2046: // the search IDs for that array are reset.
2047:
2048: if (array != null && var.isVarUndefined()) {
2049: array.sidVec = null;
2050: }
2051: */
2052: }
2053:
2054: /**
2055: * Tcl_UntraceVar2 -> untraceVar
2056: *
2057: * Untrace a variable, given a two-part name consisting of array
2058: * name and element within array. This will Remove a
2059: * previously-created trace for a variable.
2060: *
2061: * @param interp Interpreter containing variable.
2062: * @param part1 1st part of the variable name.
2063: * @param part2 2nd part of the variable name.
2064: * @param flags misc flags that control the actions of this method.
2065: * @param proc the trace to delete.
2066: */
2067:
2068: static void untraceVar(Interp interp, // Interpreter containing variable.
2069: String part1, // Name of variable or array.
2070: String part2, // Name of element within array; null means
2071: // trace applies to scalar variable or array
2072: // as-a-whole.
2073: int flags, // OR-ed collection of bits describing
2074: // current trace, including any of
2075: // TCL.TRACE_READS, TCL.TRACE_WRITES,
2076: // TCL.TRACE_UNSETS, TCL.GLOBAL_ONLY,
2077: // and TCL.NAMESPACE_ONLY.
2078: VarTrace proc // Procedure assocated with trace.
2079: ) {
2080: Var[] result = null;
2081: Var var;
2082:
2083: try {
2084: result = lookupVar(interp, part1, part2, flags
2085: & (TCL.GLOBAL_ONLY | TCL.NAMESPACE_ONLY), null,
2086: false, false);
2087: if (result == null) {
2088: return;
2089: }
2090: } catch (TclException e) {
2091: // FIXME: check for problems in exception in lookupVar
2092:
2093: // We have set throwException argument to false in the
2094: // lookupVar() call, so an exception should never be
2095: // thrown.
2096:
2097: throw new TclRuntimeError("unexpected TclException: " + e);
2098: }
2099:
2100: var = result[0];
2101:
2102: if (var.traces != null) {
2103: int len = var.traces.size();
2104: for (int i = 0; i < len; i++) {
2105: TraceRecord rec = (TraceRecord) var.traces.get(i);
2106: if (rec.trace == proc) {
2107: var.traces.remove(i);
2108: break;
2109: }
2110: }
2111:
2112: // If there are no more traces, then null
2113: // the var.traces field since logic in this
2114: // module depends on a null traces field.
2115:
2116: if (var.traces.size() == 0) {
2117: var.traces = null;
2118: var.clearVarTraceExists();
2119: }
2120: }
2121:
2122: // If this is the last trace on the variable, and the variable is
2123: // unset and unused, then free up the variable.
2124:
2125: if (var.isVarUndefined()) {
2126: cleanupVar(var, null);
2127: }
2128: }
2129:
2130: /**
2131: * Tcl_VarTraceInfo2 -> getTraces
2132: *
2133: * @return the list of traces of a variable.
2134: *
2135: * @param interp Interpreter containing variable.
2136: * @param part1 1st part of the variable name.
2137: * @param part2 2nd part of the variable name (can be null).
2138: * @param flags misc flags that control the actions of this method.
2139: */
2140:
2141: static protected ArrayList getTraces(Interp interp, // Interpreter containing variable.
2142: String part1, // Name of variable or array.
2143: String part2, // Name of element within array; null means
2144: // trace applies to scalar variable or array
2145: // as-a-whole.
2146: int flags // OR-ed combination of TCL.GLOBAL_ONLY,
2147: // TCL.NAMESPACE_ONLY.
2148: ) throws TclException {
2149: Var[] result;
2150:
2151: result = lookupVar(interp, part1, part2, flags
2152: & (TCL.GLOBAL_ONLY | TCL.NAMESPACE_ONLY), null, false,
2153: false);
2154:
2155: if (result == null) {
2156: return null;
2157: }
2158:
2159: return result[0].traces;
2160: }
2161:
2162: /**
2163: * MakeUpvar -> makeUpvar
2164: *
2165: * Create a reference of a variable in otherFrame in the current
2166: * CallFrame, given a two-part name consisting of array name and
2167: * element within array.
2168: *
2169: * @param interp Interp containing the variables
2170: * @param frame CallFrame containing "other" variable.
2171: * null means use global context.
2172: * @param otherP1 the 1st part name of the variable in the "other" frame.
2173: * @param otherP2 the 2nd part name of the variable in the "other" frame.
2174: * @param otherFlags the flags for scaope of "other" variable
2175: * @param myName Name of scalar variable which will refer to otherP1/otherP2.
2176: * @param myFlags only the TCL.GLOBAL_ONLY bit matters,
2177: * indicating the scope of myName.
2178: * @exception TclException if the upvar cannot be created.
2179: */
2180:
2181: // FIXME: Tcl 8.4 implements resolver logic specific to upvar with the flag
2182: // LOOKUP_FOR_UPVAR. Port this logic and test cases to Jacl.
2183: protected static void makeUpvar(Interp interp, // Interpreter containing variables. Used
2184: // for error messages, too.
2185: CallFrame frame, // Call frame containing "other" variable.
2186: // null means use global :: context.
2187: String otherP1, // Two-part name of variable in framePtr.
2188: String otherP2, int otherFlags, // 0, TCL.GLOBAL_ONLY or TCL.NAMESPACE_ONLY:
2189: // indicates scope of "other" variable.
2190: String myName, // Name of variable which will refer to
2191: // otherP1/otherP2. Must be a scalar.
2192: int myFlags, // 0, TCL.GLOBAL_ONLY or TCL.NAMESPACE_ONLY:
2193: // indicates scope of myName. Also accepts
2194: // the special Var.EXPLICIT_LOCAL_NAME flag
2195: // which is used to ignore namespace lookup
2196: // rules for myname.
2197: int localIndex // If != -1, this is the index into the
2198: // compiledLocals array where the upvar
2199: // variable could be stored.
2200: ) throws TclException {
2201: Var other, var, array;
2202: Var[] result = null;
2203: CallFrame varFrame;
2204: CallFrame savedFrame = null;
2205: HashMap table;
2206: Namespace ns, altNs;
2207: String tail;
2208: boolean newvar = false;
2209: boolean foundInCompiledLocalsArray = false;
2210: boolean foundInLocalTable = false;
2211: Var[] compiledLocals = null;
2212:
2213: // Find "other" in "frame". If not looking up other in just the
2214: // current namespace, temporarily replace the current var frame
2215: // pointer in the interpreter in order to use TclLookupVar.
2216:
2217: try {
2218: if ((otherFlags & TCL.NAMESPACE_ONLY) == 0) {
2219: savedFrame = interp.varFrame;
2220: interp.varFrame = frame;
2221: }
2222:
2223: // If the special EXPLICIT_LOCAL_NAME flag is passed, then
2224: // do nothing instead of raising a TclException when the
2225: // other lookup fails.
2226:
2227: int otherLookupFlags = (otherFlags | TCL.LEAVE_ERR_MSG);
2228: if ((myFlags & EXPLICIT_LOCAL_NAME) != 0) {
2229: otherLookupFlags = otherFlags;
2230: }
2231:
2232: result = Var.lookupVar(interp, otherP1, otherP2,
2233: otherLookupFlags, "access", true, true);
2234: } finally {
2235: // Reset interp.varFrame
2236:
2237: if ((otherFlags & TCL.NAMESPACE_ONLY) == 0) {
2238: interp.varFrame = savedFrame;
2239: }
2240: }
2241:
2242: if (result == null) {
2243: // EXPLICIT_LOCAL_NAME flag passed and lookup failed.
2244: return;
2245: }
2246:
2247: other = result[0];
2248: array = result[1];
2249:
2250: if (other == null) {
2251: // Should not be returned since TCL.LEAVE_ERR_MSG
2252: // was passed to generate a TclException.
2253: throw new TclRuntimeError("unexpected null reference");
2254: }
2255:
2256: // This module assumes that a link target will never
2257: // be a link var. In this way, a link var is
2258: // always resolved to either a scalar or an array
2259: // by following a single link. The lookupVar() method
2260: // should always return either a scalar or an array.
2261:
2262: if (other.isVarLink()) {
2263: throw new TclRuntimeError(
2264: "other var resolved to a link var");
2265: }
2266:
2267: // Now create a variable entry for "myName". Create it as either a
2268: // namespace variable or as a local variable in a procedure call
2269: // frame. Interpret myName as a namespace variable if:
2270: // 1) so requested by a TCL.GLOBAL_ONLY or TCL.NAMESPACE_ONLY flag,
2271: // 2) there is no active frame (we're at the global :: scope),
2272: // 3) the active frame was pushed to define the namespace context
2273: // for a "namespace eval" or "namespace inscope" command,
2274: // 4) the name has namespace qualifiers ("::"s), unless
2275: // the special EXPLICIT_LOCAL_NAME flag is set.
2276: // If creating myName in the active procedure, look in its
2277: // hashtable for runtime-created local variables. Create that
2278: // procedure's local variable hashtable if necessary.
2279:
2280: varFrame = interp.varFrame;
2281: if (((myFlags & (TCL.GLOBAL_ONLY | TCL.NAMESPACE_ONLY)) != 0)
2282: || (varFrame == null)
2283: || !varFrame.isProcCallFrame
2284: || ((myName.indexOf("::") != -1) && ((myFlags & EXPLICIT_LOCAL_NAME) == 0))) {
2285:
2286: Namespace.GetNamespaceForQualNameResult gnfqnr = interp.getnfqnResult;
2287: Namespace.getNamespaceForQualName(interp, myName, null,
2288: myFlags, gnfqnr);
2289: ns = gnfqnr.ns;
2290: altNs = gnfqnr.altNs;
2291: tail = gnfqnr.simpleName;
2292:
2293: if (ns == null) {
2294: ns = altNs;
2295: }
2296: if (ns == null) {
2297: throw new TclException(interp, "bad variable name \""
2298: + myName + "\": unknown namespace");
2299: }
2300:
2301: // Check that we are not trying to create a namespace var linked to
2302: // a local variable in a procedure. If we allowed this, the local
2303: // variable in the shorter-lived procedure frame could go away
2304: // leaving the namespace var's reference invalid.
2305:
2306: if (((otherP2 != null) ? array.ns : other.ns) == null) {
2307: throw new TclException(
2308: interp,
2309: "bad variable name \""
2310: + myName
2311: + "\": upvar won't create namespace variable that refers to procedure variable");
2312: }
2313:
2314: var = (Var) ns.varTable.get(tail);
2315: if (var == null) { // we are adding a new entry
2316: newvar = true;
2317: var = new Var();
2318: ns.varTable.put(tail, var);
2319:
2320: // There is no hPtr member in Jacl, The hPtr combines the table
2321: // and the key used in a table lookup.
2322: var.hashKey = tail;
2323: var.table = ns.varTable;
2324:
2325: var.ns = ns; // Namespace var
2326: }
2327: } else {
2328: var = null;
2329:
2330: compiledLocals = varFrame.compiledLocals;
2331: if (compiledLocals != null) { // look in compiled local array
2332: // A compiled local array is defined, so
2333: // check in the compiled local array for
2334: // a variable with this name. In the case
2335: // where the compiled local index is known,
2336: // we only need to check one index. Note
2337: // that an explicit non-local name should
2338: // be matched.
2339:
2340: if (localIndex == -1) {
2341: // compiled local slot is not known, search by name.
2342: final int MAX = compiledLocals.length;
2343: String[] compiledLocalsNames = varFrame.compiledLocalsNames;
2344:
2345: for (int i = 0; i < MAX; i++) {
2346: if (compiledLocalsNames[i].equals(myName)) {
2347: foundInCompiledLocalsArray = true;
2348: localIndex = i;
2349: var = compiledLocals[i];
2350: break;
2351: }
2352: }
2353: } else {
2354: // Slot the var should live in is known at compile time.
2355: // Check to see if compiled local var exists already.
2356:
2357: foundInCompiledLocalsArray = true;
2358: var = compiledLocals[localIndex];
2359: }
2360: }
2361:
2362: if (!foundInCompiledLocalsArray) { // look in frame's local var hashtable
2363: table = varFrame.varTable;
2364:
2365: // Note: Don't create var in the local table when it
2366: // should be created in the compiledLocals array.
2367:
2368: if (table == null) {
2369: table = new HashMap();
2370: varFrame.varTable = table;
2371: }
2372:
2373: if (table != null) {
2374: var = (Var) table.get(myName);
2375: }
2376:
2377: if (var == null) { // we are adding a new entry
2378: newvar = true;
2379: var = new Var();
2380: table.put(myName, var);
2381:
2382: var.hashKey = myName;
2383: var.table = table;
2384: }
2385: if (var != null) {
2386: foundInLocalTable = true;
2387: }
2388: }
2389:
2390: // Var should live in the compiled local
2391: // array, but it does not exist yet. Create
2392: // a new Var instance that will be assigned
2393: // to the compiled local array slot. This
2394: // Var instance will become a link var.
2395:
2396: if (foundInCompiledLocalsArray && (var == null)) {
2397: newvar = true;
2398: var = new Var();
2399: var.hashKey = myName;
2400: var.clearVarInHashtable();
2401: }
2402: }
2403:
2404: if (!newvar) {
2405: // The variable already exists. Make sure this variable "var"
2406: // isn't the same as "other" (avoid circular links). Also, if
2407: // it's not an upvar then it's an error. If it is an upvar, then
2408: // just disconnect it from the thing it currently refers to.
2409:
2410: if (var == other) {
2411: throw new TclException(interp,
2412: "can't upvar from variable to itself");
2413: }
2414: if (var.isVarLink()) {
2415: Var link = var.linkto;
2416: if (link == other) {
2417: // Already linked to the variable, no-op
2418: return;
2419: }
2420: link.refCount--;
2421: if (link.isVarUndefined()) {
2422: cleanupVar(link, null);
2423: }
2424: } else if (!var.isVarUndefined()) {
2425: throw new TclException(interp, "variable \"" + myName
2426: + "\" already exists");
2427: } else if (var.traces != null) {
2428: throw new TclException(interp, "variable \"" + myName
2429: + "\" has traces: can't use for upvar");
2430: }
2431: // FIXME: Is it possible that the other var
2432: // would not be a var link type but it would
2433: // be undefined waiting to be cleaned up?
2434: // For example, a linked var in another scope?
2435: // Add a test case for this.
2436: }
2437:
2438: var.setVarLink();
2439: var.clearVarUndefined();
2440: var.linkto = other;
2441: other.refCount++;
2442:
2443: // If the link var should be stored in the compiledLocals
2444: // array then do that now. A variable with this same
2445: // name would never appear in the local table. Also mark
2446: // this variable as non-local for scoped global.
2447:
2448: if (foundInCompiledLocalsArray) {
2449: if (newvar) {
2450: compiledLocals[localIndex] = var;
2451: }
2452:
2453: if ((myFlags & EXPLICIT_LOCAL_NAME) != 0) {
2454: var.setVarNonLocal();
2455: }
2456: }
2457:
2458: return;
2459: }
2460:
2461: /*
2462: *----------------------------------------------------------------------
2463: *
2464: * Tcl_GetVariableFullName -> getVariableFullName
2465: *
2466: * Given a Var token returned by Namespace.FindNamespaceVar, this
2467: * procedure appends to an object the namespace variable's full
2468: * name, qualified by a sequence of parent namespace names.
2469: *
2470: * Results:
2471: * None.
2472: *
2473: * Side effects:
2474: * The variable's fully-qualified name is returned.
2475: *
2476: *----------------------------------------------------------------------
2477: */
2478:
2479: public static String getVariableFullName(Interp interp, // Interpreter containing the variable.
2480: Var var // Token for the variable returned by a
2481: // previous call to Tcl_FindNamespaceVar.
2482: ) {
2483: StringBuffer buff = new StringBuffer();
2484:
2485: // Add the full name of the containing namespace (if any), followed by
2486: // the "::" separator, then the variable name.
2487:
2488: if (var != null) {
2489: if (!var.isVarArrayElement()) {
2490: if (var.ns != null) {
2491: buff.append(var.ns.fullName);
2492: if (var.ns != interp.globalNs) {
2493: buff.append("::");
2494: }
2495: }
2496: // Jacl's Var class does not include the "name" member
2497: // We use the "hashKey" member which is equivalent
2498:
2499: if (var.hashKey != null) {
2500: buff.append(var.hashKey);
2501: }
2502: }
2503: }
2504:
2505: return buff.toString();
2506: }
2507:
2508: /**
2509: * CallTraces -> callTraces
2510: *
2511: * This procedure is invoked to find and invoke relevant
2512: * trace procedures associated with a particular operation on
2513: * a variable. This procedure invokes traces both on the
2514: * variable and on its containing array (where relevant).
2515: *
2516: * @param interp Interpreter containing variable.
2517: * @param array array variable that contains the variable, or null
2518: * if the variable isn't an element of an array.
2519: * @param var Variable whose traces are to be invoked.
2520: * @param part1 the first part of a variable name.
2521: * @param part2 the second part of a variable name.
2522: * @param flags Flags to pass to trace procedures: indicates
2523: * what's happening to variable, plus other stuff like
2524: * TCL.GLOBAL_ONLY, TCL.NAMESPACE_ONLY, and TCL.INTERP_DESTROYED.
2525: * @return null if no trace procedures were invoked, or
2526: * if all the invoked trace procedures returned successfully.
2527: * The return value is non-null if a trace procedure returned an
2528: * error (in this case no more trace procedures were invoked
2529: * after the error was returned). In this case the return value
2530: * is a pointer to a string describing the error.
2531: */
2532:
2533: static protected String callTraces(Interp interp, Var array,
2534: Var var, String part1, String part2, int flags) {
2535: TclObject oldResult;
2536: int i;
2537:
2538: // If there are already similar trace procedures active for the
2539: // variable, don't call them again.
2540:
2541: if ((var.flags & Var.TRACE_ACTIVE) != 0) {
2542: return null;
2543: }
2544: var.flags |= Var.TRACE_ACTIVE;
2545: var.refCount++;
2546:
2547: // If the variable name hasn't been parsed into array name and
2548: // element, do it here. If there really is an array element,
2549: // make a copy of the original name so that nulls can be
2550: // inserted into it to separate the names (can't modify the name
2551: // string in place, because the string might get used by the
2552: // callbacks we invoke).
2553:
2554: // FIXME : a util method that parsed an array variable
2555: // name into array and element component parts could
2556: // be useful. There are a number of places where this
2557: // is done inline in the code.
2558: if (part2 == null) {
2559: int len = part1.length();
2560:
2561: if (len > 0) {
2562: if (part1.charAt(len - 1) == ')') {
2563: for (i = 0; i < len - 1; i++) {
2564: if (part1.charAt(i) == '(') {
2565: break;
2566: }
2567: }
2568: if (i < len - 1) {
2569: if (i < len - 2) {
2570: part2 = part1.substring(i + 1, len - 1);
2571: part1 = part1.substring(0, i);
2572: }
2573: }
2574: }
2575: }
2576: }
2577:
2578: oldResult = interp.getResult();
2579: oldResult.preserve();
2580: interp.resetResult();
2581:
2582: try {
2583: // Invoke traces on the array containing the variable, if relevant.
2584:
2585: if (array != null) {
2586: array.refCount++;
2587: }
2588: if ((array != null) && (array.traces != null)) {
2589: for (i = 0; (array.traces != null)
2590: && (i < array.traces.size()); i++) {
2591: TraceRecord rec = (TraceRecord) array.traces.get(i);
2592: if ((rec.flags & flags) != 0) {
2593: try {
2594: rec.trace.traceProc(interp, part1, part2,
2595: flags);
2596: } catch (TclException e) {
2597: if ((flags & TCL.TRACE_UNSETS) == 0) {
2598: return interp.getResult().toString();
2599: }
2600: }
2601: }
2602: }
2603: }
2604:
2605: // Invoke traces on the variable itself.
2606:
2607: if ((flags & TCL.TRACE_UNSETS) != 0) {
2608: flags |= TCL.TRACE_DESTROYED;
2609: }
2610:
2611: for (i = 0; (var.traces != null) && (i < var.traces.size()); i++) {
2612: TraceRecord rec = (TraceRecord) var.traces.get(i);
2613: if ((rec.flags & flags) != 0) {
2614: try {
2615: rec.trace
2616: .traceProc(interp, part1, part2, flags);
2617: } catch (TclException e) {
2618: if ((flags & TCL.TRACE_UNSETS) == 0) {
2619: return interp.getResult().toString();
2620: }
2621: }
2622: }
2623: }
2624:
2625: return null;
2626: } finally {
2627: if (array != null) {
2628: array.refCount--;
2629: }
2630: var.flags &= ~TRACE_ACTIVE;
2631: var.refCount--;
2632:
2633: interp.setResult(oldResult);
2634: oldResult.release();
2635: }
2636: }
2637:
2638: /**
2639: * DeleteSearches -> deleteSearches
2640: *
2641: * This procedure is called to free up all of the searches
2642: * associated with an array variable.
2643: *
2644: * @param interp Interpreter containing array.
2645: * @param arrayVar the array variable to delete searches from.
2646: */
2647:
2648: static protected void deleteSearches(Var arrayVar) // Variable whose searches are to be deleted.
2649: {
2650: arrayVar.sidVec = null;
2651: }
2652:
2653: /**
2654: * TclDeleteVars -> deleteVars
2655: *
2656: * This procedure is called to recycle all the storage space
2657: * associated with a table of variables. For this procedure
2658: * to work correctly, it must not be possible for any of the
2659: * variables in the table to be accessed from Tcl commands
2660: * (e.g. from trace procedures).
2661: *
2662: * @param interp Interpreter containing array.
2663: * @param table HashMap that holds the Vars to delete
2664: */
2665:
2666: static protected void deleteVars(Interp interp, HashMap table) {
2667: int flags;
2668: Namespace currNs = Namespace.getCurrentNamespace(interp);
2669:
2670: // Determine what flags to pass to the trace callback procedures.
2671:
2672: flags = TCL.TRACE_UNSETS;
2673: if (table == interp.globalNs.varTable) {
2674: flags |= (TCL.INTERP_DESTROYED | TCL.GLOBAL_ONLY);
2675: } else if (table == currNs.varTable) {
2676: flags |= TCL.NAMESPACE_ONLY;
2677: }
2678:
2679: for (Iterator iter = table.entrySet().iterator(); iter
2680: .hasNext();) {
2681: Map.Entry entry = (Map.Entry) iter.next();
2682: deleteVar(interp, (Var) entry.getValue(), flags);
2683: }
2684: table.clear();
2685: }
2686:
2687: /**
2688: // FIXME: Make more like TclDeleteCompiledLocalVars()
2689: * TclDeleteVars -> deleteVars
2690: *
2691: * This procedure is called to recycle all the storage space
2692: * associated with an array of variables. For this procedure
2693: * to work correctly, it must not be possible for any of the
2694: * variables in the array to be accessed from Tcl commands
2695: * (e.g. from trace procedures).
2696: *
2697: * @param interp Interpreter containing array.
2698: * @param compiledLocals array of compiled local variables
2699: */
2700:
2701: static protected void deleteVars(Interp interp, Var[] compiledLocals) {
2702: // A compiled local array only ever exists for a compiled
2703: // proc, so flags is always the same.
2704:
2705: final int flags = TCL.TRACE_UNSETS;
2706:
2707: final int max = compiledLocals.length;
2708:
2709: for (int i = 0; i < max; i++) {
2710: Var clocal = compiledLocals[i];
2711: if (clocal != null) {
2712: // Cleanup the Var instance and then
2713: // null out the compiled local slots.
2714:
2715: deleteVar(interp, clocal, flags);
2716: compiledLocals[i] = null;
2717: }
2718: }
2719: }
2720:
2721: /**
2722: * deleteVar
2723: *
2724: * This procedure is called to recycle all the storage space
2725: * associated with a single Var instance.
2726: *
2727: * @param interp Interpreter containing array.
2728: * @param var A Var refrence to be deleted
2729: * @param flags flags to pass to trace callbacks.
2730: */
2731:
2732: static protected void deleteVar(Interp interp, Var var, int flags) {
2733: // For global/upvar variables referenced in procedures, decrement
2734: // the reference count on the variable referred to, and free
2735: // the referenced variable if it's no longer needed. Note that
2736: // we always delete the link in another table, this should be
2737: // fine since this method is invoked after the regular variables
2738: // are deleted.
2739:
2740: if ((var.flags & LINK) != 0) {
2741: // Follow link to either scalar or array variable
2742: Var link = var.linkto;
2743: link.refCount--;
2744: if ((link.refCount == 0)
2745: && (link.traces == null)
2746: // (link.isVarUndefined() && link.isVarInHashtable())
2747: && ((link.flags & (UNDEFINED | IN_HASHTABLE)) == (UNDEFINED | IN_HASHTABLE))) {
2748: if (link.hashKey == null) {
2749: var.linkto = null; // Drops reference to the link Var
2750: } else if (link.table != var.table) {
2751: link.table.remove(link.hashKey);
2752: link.table = null; // Drops the link var's table reference
2753: var.linkto = null; // Drops reference to the link Var
2754: }
2755: }
2756: }
2757:
2758: // free up the variable's space (no need to free the hash entry
2759: // here, unless we're dealing with a global variable: the
2760: // hash entries will be deleted automatically when the whole
2761: // table is deleted). Note that we give callTraces the variable's
2762: // fully-qualified name so that any called trace procedures can
2763: // refer to these variables being deleted.
2764:
2765: if (var.traces != null) {
2766: String fullname = getVariableFullName(interp, var);
2767:
2768: callTraces(interp, null, var, fullname, null, flags);
2769:
2770: // The var.traces = null statement later will drop all the
2771: // references to the traces which will free them up
2772: }
2773:
2774: if ((var.flags & ARRAY) != 0) {
2775: deleteArray(interp, var.hashKey, var, flags);
2776: var.arraymap = null;
2777: } else if (((var.flags & SCALAR) != 0) && (var.tobj != null)) {
2778: TclObject obj = var.tobj;
2779: obj.release();
2780: var.tobj = null;
2781: }
2782:
2783: var.hashKey = null;
2784: var.table = null;
2785: var.traces = null;
2786: var.setVarUndefined();
2787: var.setVarScalar();
2788:
2789: // If the variable was a namespace variable, decrement its
2790: // reference count. We are in the process of destroying its
2791: // namespace so that namespace will no longer "refer" to the
2792: // variable.
2793:
2794: if ((var.flags & NAMESPACE_VAR) != 0) {
2795: var.flags &= ~NAMESPACE_VAR;
2796: var.refCount--;
2797: }
2798:
2799: // Recycle the variable's memory space if there aren't any upvar's
2800: // pointing to it. If there are upvars to this variable, then the
2801: // variable will get freed when the last upvar goes away. This
2802: // variable could still be alive after this method finishes since
2803: // it could be refrenced in a namespace. The code will catch
2804: // that case by looking at the IN_HASHTABLE flag and seeing
2805: // if the table member is null.
2806:
2807: // if (var.refCount == 0) {
2808: // // When we drop the last reference it will be freeded
2809: // }
2810: }
2811:
2812: /**
2813: * DeleteArray -> deleteArray
2814: *
2815: * This procedure is called to free up everything in an array
2816: * variable. It's the caller's responsibility to make sure
2817: * that the array is no longer accessible before this procedure
2818: * is called.
2819: *
2820: * @param interp Interpreter containing array.
2821: * @param arrayName name of array (used for trace callbacks).
2822: * @param var the array variable to delete.
2823: * @param flags Flags to pass to CallTraces.
2824: */
2825:
2826: static protected void deleteArray(Interp interp, // Interpreter containing array.
2827: String arrayName, // Name of array (used for trace callbacks)
2828: Var var, // Reference to Var instance
2829: int flags // Flags to pass to callTraces:
2830: // TCL.TRACE_UNSETS and sometimes
2831: // TCL.INTERP_DESTROYED,
2832: // TCL.NAMESPACE_ONLY, or
2833: // TCL.GLOBAL_ONLY.
2834: ) {
2835: Var el;
2836: TclObject obj;
2837:
2838: deleteSearches(var);
2839: HashMap table = var.arraymap;
2840:
2841: for (Iterator iter = table.entrySet().iterator(); iter
2842: .hasNext();) {
2843: Map.Entry entry = (Map.Entry) iter.next();
2844: //String key = (String) entry.getKey();
2845: el = (Var) entry.getValue();
2846:
2847: if (el.isVarScalar() && (el.tobj != null)) {
2848: obj = el.tobj;
2849: obj.release();
2850: el.tobj = null;
2851: }
2852:
2853: String tmpkey = (String) el.hashKey;
2854: // There is no hPtr member in Jacl, The hPtr combines the table
2855: // and the key used in a table lookup.
2856: el.hashKey = null;
2857: el.table = null;
2858: if (el.traces != null) {
2859: el.flags &= ~TRACE_ACTIVE;
2860: // FIXME : Old Jacl impl passed a dummy var to callTraces, should we?
2861: callTraces(interp, null, el, arrayName, tmpkey, flags);
2862: el.traces = null;
2863: // Active trace stuff is not part of Jacl
2864: }
2865: el.setVarUndefined();
2866: el.setVarScalar();
2867: if (el.refCount == 0) {
2868: // We are no longer using the element
2869: // element Vars are IN_HASHTABLE
2870: }
2871: }
2872: table.clear();
2873: var.arraymap = null;
2874: }
2875:
2876: /**
2877: * CleanupVar -> cleanupVar
2878: *
2879: * This procedure is called when it looks like it may be OK
2880: * to free up the variable's record and hash table entry, and
2881: * those of its containing parent. It's called, for example,
2882: * when a trace on a variable deletes the variable.
2883: *
2884: * @param var variable that may be a candidate for being expunged.
2885: * @param array Array that contains the variable, or NULL if this
2886: * variable isn't an array element.
2887: */
2888:
2889: static protected void cleanupVar(Var var, Var array) {
2890: if (var.isVarUndefined() && (var.refCount == 0)
2891: && (var.traces == null)
2892: && ((var.flags & IN_HASHTABLE) != 0)) {
2893: if (var.table != null) {
2894: var.table.remove(var.hashKey);
2895: var.table = null;
2896: var.hashKey = null;
2897: }
2898: }
2899: if (array != null) {
2900: if (array.isVarUndefined() && (array.refCount == 0)
2901: && (array.traces == null)
2902: && ((array.flags & IN_HASHTABLE) != 0)) {
2903: if (array.table != null) {
2904: array.table.remove(array.hashKey);
2905: array.table = null;
2906: array.hashKey = null;
2907: }
2908: }
2909: }
2910: }
2911:
2912: // CompiledLocal utilitiy methods.
2913:
2914: // The resolveScalar() method will
2915: // attempt to resolve a scalar Var ref
2916: // in the compiled local array.
2917: // If the Var can't be resolved as
2918: // a valid scalar, then null will
2919: // be returned.
2920: //
2921: // A resolved scalar is invalid when any
2922: // of the following conditions is true.
2923: //
2924: // When the variable is not a scalar.
2925: // When the variable is an array element.
2926: // When the variable is undefined.
2927: // When the variable has traces.
2928: // When the variable can't be cached.
2929:
2930: static Var resolveScalar(Var v) {
2931: int flags = v.flags;
2932: if ((flags & LINK) != 0) {
2933: v = v.linkto;
2934: flags = v.flags;
2935: }
2936:
2937: // Can't resolve var if it is not a scalar, if it is
2938: // an array element, if it is undefined, if it has traces,
2939: // or it can't be cached because it is a resolver var.
2940:
2941: if ((flags & (LINK | ARRAY | ARRAY_ELEMENT | UNDEFINED
2942: | TRACE_EXISTS | NO_CACHE)) != 0) {
2943: return null;
2944: }
2945: return v;
2946: }
2947:
2948: // The resolveArray() method will
2949: // attempt to resolve an array Var ref
2950: // in the compiled local array.
2951: // If the Var can't be resolved as
2952: // a valid array variable, then null will
2953: // be returned.
2954: //
2955: // A resolved array is invalid when any
2956: // of the following conditions is true.
2957: //
2958: // When the variable is not an array.
2959: // When the variable is undefined.
2960: //
2961: // Note that an array with traces is
2962: // considered valid since the array
2963: // methods support invoking traces.
2964:
2965: static Var resolveArray(Var v) {
2966: int flags = v.flags;
2967: if ((flags & LINK) != 0) {
2968: v = v.linkto;
2969: flags = v.flags;
2970: }
2971:
2972: // Can't resolve var if it is not an array,
2973: // if it is undefined, or it can't be cached
2974: // because it is a resolver var. An array
2975: // var with traces can be resolved.
2976:
2977: if ((flags & (LINK | SCALAR | UNDEFINED | NO_CACHE)) != 0) {
2978: return null;
2979: }
2980: return v;
2981: }
2982:
2983: // Helper method invoked to null out a compiled local
2984: // slot for a non-linked local variable that is now
2985: // undefined. The unset command has no way of knowing
2986: // if a variable being unset lives in the compiled
2987: // local array, so this method is invoked after
2988: // each unset operation to keep the compiled local
2989: // array up to date. Variables linked into another
2990: // scope can be undefined, so ignore those.
2991:
2992: static void setUndefinedToNull(Interp interp, String part1,
2993: String part2) {
2994: CallFrame varFrame = interp.varFrame;
2995: if (varFrame == null) {
2996: return; // Invoked from global scope
2997: }
2998:
2999: if (varFrame.compiledLocals != null) {
3000: Var[] compiledLocals = varFrame.compiledLocals;
3001: final int MAX = compiledLocals.length;
3002:
3003: for (int i = 0; i < MAX; i++) {
3004: Var clocal = compiledLocals[i];
3005: if (clocal != null && !clocal.isVarNonLocal()
3006: && clocal.hashKey.equals(part1)) {
3007: if (!clocal.isVarLink() && clocal.isVarUndefined()) {
3008: // Set the compiled local slot to null
3009: // if there are no other vars linked to
3010: // this var.
3011: if (clocal.refCount == 0) {
3012: compiledLocals[i] = null;
3013: }
3014: }
3015: return;
3016: }
3017: }
3018: }
3019: }
3020:
3021: /*
3022: *----------------------------------------------------------------------
3023: *
3024: * AppendLocals --
3025: *
3026: * Append the local variables for the current frame to the
3027: * specified list object. This method is used by InfoCmd.
3028: *
3029: * Results:
3030: * None.
3031: *
3032: * Side effects:
3033: * None.
3034: *
3035: *----------------------------------------------------------------------
3036: */
3037:
3038: static void AppendLocals(Interp interp, // Current interp
3039: TclObject list, // list to append to
3040: String pattern, // Pattern to match against.
3041: boolean includeLinks) // true if upvars should be included
3042: throws TclException {
3043: Var var;
3044: String varName;
3045: CallFrame frame = interp.varFrame;
3046:
3047: HashMap localVarTable = frame.varTable;
3048: if (localVarTable != null) {
3049: for (Iterator iter = localVarTable.entrySet().iterator(); iter
3050: .hasNext();) {
3051: Map.Entry entry = (Map.Entry) iter.next();
3052: varName = (String) entry.getKey();
3053: var = (Var) entry.getValue();
3054: if (!var.isVarUndefined()
3055: && (includeLinks || !var.isVarLink())) {
3056: if ((pattern == null)
3057: || Util.stringMatch(varName, pattern)) {
3058: TclList.append(interp, list, TclString
3059: .newInstance(varName));
3060: }
3061: }
3062: }
3063: }
3064:
3065: Var[] compiledLocals = frame.compiledLocals;
3066: if (compiledLocals != null) {
3067: final int max = compiledLocals.length;
3068: for (int i = 0; i < max; i++) {
3069: Var clocal = compiledLocals[i];
3070: if (clocal != null && !clocal.isVarNonLocal()) {
3071: var = clocal;
3072: varName = (String) var.hashKey;
3073:
3074: if (!var.isVarUndefined()
3075: && (includeLinks || !var.isVarLink())) {
3076: if ((pattern == null)
3077: || Util.stringMatch(varName, pattern)) {
3078: TclList.append(interp, list, TclString
3079: .newInstance(varName));
3080: }
3081: }
3082: }
3083: }
3084: }
3085: }
3086:
3087: } // End of Var class
|