internal Procedure( Interp interp, NamespaceCmd.Namespace ns, string name, TclObject args, TclObject b, string sFileName, int sLineNumber ) { this.ns = ns; srcFileName = sFileName; srcLineNumber = sLineNumber; // Break up the argument list into argument specifiers, then process // each argument specifier. int numArgs = TclList.getLength( interp, args ); argList = new TclObject[numArgs][]; for ( int i = 0; i < numArgs; i++ ) { argList[i] = new TclObject[2]; } for ( int i = 0; i < numArgs; i++ ) { // Now divide the specifier up into name and default. TclObject argSpec = TclList.index( interp, args, i ); int specLen = TclList.getLength( interp, argSpec ); if ( specLen == 0 ) { throw new TclException( interp, "procedure \"" + name + "\" has argument with no name" ); } if ( specLen > 2 ) { throw new TclException( interp, "too many fields in argument " + "specifier \"" + argSpec + "\"" ); } argList[i][0] = TclList.index( interp, argSpec, 0 ); argList[i][0].preserve(); if ( specLen == 2 ) { argList[i][1] = TclList.index( interp, argSpec, 1 ); argList[i][1].preserve(); } else { argList[i][1] = null; } } if ( numArgs > 0 && ( argList[numArgs - 1][0].ToString().Equals( "args" ) ) ) { isVarArgs = true; } else { isVarArgs = false; } body = new CharPointer( b.ToString() ); body_length = body.length(); }
/// <summary> TclLookupVar -> lookupVar /// /// This procedure is used by virtually all of the variable /// code to locate a variable given its name(s). /// /// </summary> /// <param name="part1">if part2 isn't NULL, this is the name of an array. /// Otherwise, this is a full variable name that could include /// a parenthesized array elemnt or a scalar. /// </param> /// <param name="part2">Name of an element within array, or null. /// </param> /// <param name="flags">Only the TCL.VarFlag.GLOBAL_ONLY bit matters. /// </param> /// <param name="msg">Verb to use in error messages, e.g. "read" or "set". /// </param> /// <param name="create">OR'ed combination of CRT_PART1 and CRT_PART2. /// Tells which entries to create if they don't already exist. /// </param> /// <param name="throwException">true if an exception should be throw if the /// variable cannot be found. /// </param> /// <returns> a two element array. a[0] is the variable indicated by /// part1 and part2, or null if the variable couldn't be /// found and throwException is false. /// <p> /// If the variable is found, a[1] is the array that /// contains the variable (or null if the variable is a scalar). /// If the variable can't be found and either createPart1 or /// createPart2 are true, a new as-yet-undefined (VAR_UNDEFINED) /// variable instance is created, entered into a hash /// table, and returned. /// Note: it's possible that var.value of the returned variable /// may be null (variable undefined), even if createPart1 or createPart2 /// are true (these only cause the hash table entry or array to be created). /// For example, the variable might be a global that has been unset but /// is still referenced by a procedure, or a variable that has been unset /// but it only being kept in existence by a trace. /// </returns> /// <exception cref=""> TclException if the variable cannot be found and /// throwException is true. /// /// </exception> internal static Var[] lookupVar( Interp interp, string part1, string part2, TCL.VarFlag flags, string msg, bool createPart1, bool createPart2 ) { CallFrame varFrame = interp.varFrame; // Reference to the procedure call frame whose // variables are currently in use. Same as // the current procedure's frame, if any, // unless an "uplevel" is executing. Hashtable table; // to the hashtable, if any, in which // to look up the variable. Var var; // Used to search for global names. string elName; // Name of array element or null. int openParen; // If this procedure parses a name into // array and index, these point to the // parens around the index. Otherwise they // are -1. These are needed to restore // the parens after parsing the name. NamespaceCmd.Namespace varNs, cxtNs; int p; int i, result; var = null; openParen = -1; varNs = null; // set non-null if a nonlocal variable // Parse part1 into array name and index. // Always check if part1 is an array element name and allow it only if // part2 is not given. // (if one does not care about creating array elements that can't be used // from tcl, and prefer slightly better performance, one can put // the following in an if (part2 == null) { ... } block and remove // the part2's test and error reporting or move that code in array set) elName = part2; int len = part1.Length; for ( p = 0; p < len; p++ ) { if ( part1[p] == '(' ) { openParen = p; p = len - 1; if ( part1[p] == ')' ) { if ( (System.Object)part2 != null ) { if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 ) { throw new TclVarException( interp, part1, part2, msg, needArray ); } return null; } elName = part1.Substring( openParen + 1, ( len - 1 ) - ( openParen + 1 ) ); part2 = elName; // same as elName, only used in error reporting part1 = part1.Substring( 0, ( openParen ) - ( 0 ) ); } break; } } // If this namespace has a variable resolver, then give it first // crack at the variable resolution. It may return a Var // value, it may signal to continue onward, or it may signal // an error. if ( ( ( flags & TCL.VarFlag.GLOBAL_ONLY ) != 0 ) || ( interp.varFrame == null ) ) { cxtNs = interp.globalNs; } else { cxtNs = interp.varFrame.ns; } if ( cxtNs.resolver != null || interp.resolvers != null ) { try { if ( cxtNs.resolver != null ) { var = cxtNs.resolver.resolveVar( interp, part1, cxtNs, flags ); } else { var = null; } if ( var == null && interp.resolvers != null ) { IEnumerator enum_Renamed = interp.resolvers.GetEnumerator(); foreach ( Interp.ResolverScheme res in interp.resolvers ) { var = res.resolver.resolveVar( interp, part1, cxtNs, flags ); if ( var != null ) break; } } } catch ( TclException e ) { var = null; } } // Look up part1. Look it up as either a namespace variable or as a // local variable in a procedure call frame (varFrame). // Interpret part1 as a namespace variable if: // 1) so requested by a TCL.VarFlag.GLOBAL_ONLY or TCL.VarFlag.NAMESPACE_ONLY flag, // 2) there is no active frame (we're at the global :: scope), // 3) the active frame was pushed to define the namespace context // for a "namespace eval" or "namespace inscope" command, // 4) the name has namespace qualifiers ("::"s). // Otherwise, if part1 is a local variable, search first in the // frame's array of compiler-allocated local variables, then in its // hashtable for runtime-created local variables. // // If createPart1 and the variable isn't found, create the variable and, // if necessary, create varFrame's local var hashtable. if ( ( ( flags & ( TCL.VarFlag.GLOBAL_ONLY | TCL.VarFlag.NAMESPACE_ONLY ) ) != 0 ) || ( varFrame == null ) || !varFrame.isProcCallFrame || ( part1.IndexOf( "::" ) != -1 ) ) { string tail; // Don't pass TCL.VarFlag.LEAVE_ERR_MSG, we may yet create the variable, // or otherwise generate our own error! var = NamespaceCmd.findNamespaceVar( interp, part1, null, flags & ~TCL.VarFlag.LEAVE_ERR_MSG ); if ( var == null ) { if ( createPart1 ) { // var wasn't found so create it // Java does not support passing an address so we pass // an array of size 1 and then assign arr[0] to the value NamespaceCmd.Namespace[] varNsArr = new NamespaceCmd.Namespace[1]; NamespaceCmd.Namespace[] dummyArr = new NamespaceCmd.Namespace[1]; string[] tailArr = new string[1]; NamespaceCmd.getNamespaceForQualName( interp, part1, null, flags, varNsArr, dummyArr, dummyArr, tailArr ); // Get the values out of the arrays! varNs = varNsArr[0]; tail = tailArr[0]; if ( varNs == null ) { if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 ) { throw new TclVarException( interp, part1, part2, msg, badNamespace ); } return null; } if ( (System.Object)tail == null ) { if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 ) { throw new TclVarException( interp, part1, part2, msg, missingName ); } return null; } var = new Var(); varNs.varTable.Add( tail, var ); // There is no hPtr member in Jacl, The hPtr combines the table // and the key used in a table lookup. var.hashKey = tail; var.table = varNs.varTable; var.ns = varNs; } else { // var wasn't found and not to create it if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 ) { throw new TclVarException( interp, part1, part2, msg, noSuchVar ); } return null; } } } else { // local var: look in frame varFrame // removed code block that searches for local compiled vars if ( var == null ) { // look in the frame's var hash table table = varFrame.varTable; if ( createPart1 ) { if ( table == null ) { table = new Hashtable(); varFrame.varTable = table; } var = (Var)table[part1]; if ( var == null ) { // we are adding a new entry var = new Var(); SupportClass.PutElement( table, part1, var ); // There is no hPtr member in Jacl, The hPtr combines // the table and the key used in a table lookup. var.hashKey = part1; var.table = table; var.ns = null; // a local variable } } else { if ( table != null ) { var = (Var)table[part1]; } if ( var == null ) { if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 ) { throw new TclVarException( interp, part1, part2, msg, noSuchVar ); } return null; } } } } // If var is a link variable, we have a reference to some variable // that was created through an "upvar" or "global" command. Traverse // through any links until we find the referenced variable. while ( var.isVarLink() ) { var = (Var)var.value; } // If we're not dealing with an array element, return var. if ( (System.Object)elName == null ) { var ret = new Var[2]; ret[0] = var; ret[1] = null; return ret; } // We're dealing with an array element. Make sure the variable is an // array and look up the element (create the element if desired). if ( var.isVarUndefined() && !var.isVarArrayElement() ) { if ( !createPart1 ) { if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 ) { throw new TclVarException( interp, part1, part2, msg, noSuchVar ); } return null; } // Make sure we are not resurrecting a namespace variable from a // deleted namespace! if ( ( ( var.flags & VarFlags.IN_HASHTABLE ) != 0 ) && ( var.table == null ) ) { if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 ) { throw new TclVarException( interp, part1, part2, msg, danglingVar ); } return null; } var.setVarArray(); var.clearVarUndefined(); var.value = new Hashtable(); } else if ( !var.isVarArray() ) { if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 ) { throw new TclVarException( interp, part1, part2, msg, needArray ); } return null; } Var arrayVar = var; Hashtable arrayTable = (Hashtable)var.value; if ( createPart2 ) { Var searchvar = (Var)arrayTable[elName]; if ( searchvar == null ) { // new entry if ( var.sidVec != null ) { deleteSearches( var ); } var = new Var(); SupportClass.PutElement( arrayTable, elName, var ); // There is no hPtr member in Jacl, The hPtr combines the table // and the key used in a table lookup. var.hashKey = elName; var.table = arrayTable; var.ns = varNs; var.setVarArrayElement(); } else { var = searchvar; } } else { var = (Var)arrayTable[elName]; if ( var == null ) { if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 ) { throw new TclVarException( interp, part1, part2, msg, noSuchElement ); } return null; } } var ret2 = new Var[2]; ret2[0] = var; // The Var in the array ret2[1] = arrayVar; // The array (Hashtable) Var return ret2; }
/// <summary> NewVar -> Var /// /// Construct a variable and initialize its fields. /// </summary> internal Var() { value = null; //name = null; // Like hashKey in Jacl ns = null; hashKey = null; // Like hPtr in the C implementation table = null; // Like hPtr in the C implementation refCount = 0; traces = null; //search = null; sidVec = null; // Like search in the C implementation flags = ( VarFlags.SCALAR | VarFlags.UNDEFINED | VarFlags.IN_HASHTABLE ); }
/// <summary> MakeUpvar -> makeUpvar /// /// Create a reference of a variable in otherFrame in the current /// CallFrame, given a two-part name consisting of array name and /// element within array. /// /// </summary> /// <param name="interp">Interp containing the variables /// </param> /// <param name="frame">CallFrame containing "other" variable. /// null means use global context. /// </param> /// <param name="otherP1">the 1st part name of the variable in the "other" frame. /// </param> /// <param name="otherP2">the 2nd part name of the variable in the "other" frame. /// </param> /// <param name="otherFlags">the flags for scaope of "other" variable /// </param> /// <param name="myName">Name of scalar variable which will refer to otherP1/otherP2. /// </param> /// <param name="myFlags">only the TCL.VarFlag.GLOBAL_ONLY bit matters, /// indicating the scope of myName. /// </param> /// <exception cref=""> TclException if the upvar cannot be created. /// </exception> protected internal static void makeUpvar( Interp interp, CallFrame frame, string otherP1, string otherP2, TCL.VarFlag otherFlags, string myName, TCL.VarFlag myFlags ) { Var other, var, array; Var[] result; CallFrame varFrame; CallFrame savedFrame = null; Hashtable table; NamespaceCmd.Namespace ns, altNs; string tail; bool newvar = false; // Find "other" in "frame". If not looking up other in just the // current namespace, temporarily replace the current var frame // pointer in the interpreter in order to use TclLookupVar. if ( ( otherFlags & TCL.VarFlag.NAMESPACE_ONLY ) == 0 ) { savedFrame = interp.varFrame; interp.varFrame = frame; } result = lookupVar( interp, otherP1, otherP2, ( otherFlags | TCL.VarFlag.LEAVE_ERR_MSG ), "access", true, true ); if ( ( otherFlags & TCL.VarFlag.NAMESPACE_ONLY ) == 0 ) { interp.varFrame = savedFrame; } other = result[0]; array = result[1]; if ( other == null ) { // FIXME : leave error message thing again throw new TclRuntimeError( "unexpected null reference" ); } // Now create a hashtable entry for "myName". Create it as either a // namespace variable or as a local variable in a procedure call // frame. Interpret myName as a namespace variable if: // 1) so requested by a TCL.VarFlag.GLOBAL_ONLY or TCL.VarFlag.NAMESPACE_ONLY flag, // 2) there is no active frame (we're at the global :: scope), // 3) the active frame was pushed to define the namespace context // for a "namespace eval" or "namespace inscope" command, // 4) the name has namespace qualifiers ("::"s). // If creating myName in the active procedure, look in its // hashtable for runtime-created local variables. Create that // procedure's local variable hashtable if necessary. varFrame = interp.varFrame; if ( ( ( myFlags & ( TCL.VarFlag.GLOBAL_ONLY | TCL.VarFlag.NAMESPACE_ONLY ) ) != 0 ) || ( varFrame == null ) || !varFrame.isProcCallFrame || ( myName.IndexOf( "::" ) != -1 ) ) { // Java does not support passing an address so we pass // an array of size 1 and then assign arr[0] to the value NamespaceCmd.Namespace[] nsArr = new NamespaceCmd.Namespace[1]; NamespaceCmd.Namespace[] altNsArr = new NamespaceCmd.Namespace[1]; NamespaceCmd.Namespace[] dummyNsArr = new NamespaceCmd.Namespace[1]; string[] tailArr = new string[1]; NamespaceCmd.getNamespaceForQualName( interp, myName, null, myFlags, nsArr, altNsArr, dummyNsArr, tailArr ); // Get the values out of the arrays! ns = nsArr[0]; altNs = altNsArr[0]; tail = tailArr[0]; if ( ns == null ) { ns = altNs; } if ( ns == null ) { throw new TclException( interp, "bad variable name \"" + myName + "\": unknown namespace" ); } // Check that we are not trying to create a namespace var linked to // a local variable in a procedure. If we allowed this, the local // variable in the shorter-lived procedure frame could go away // leaving the namespace var's reference invalid. if ( ( ( (System.Object)otherP2 != null ) ? array.ns : other.ns ) == null ) { throw new TclException( interp, "bad variable name \"" + myName + "\": upvar won't create namespace variable that refers to procedure variable" ); } // AKT var = (Var) ns.varTable.get(tail); var = (Var)ns.varTable[tail]; if ( var == null ) { // we are adding a new entry newvar = true; var = new Var(); // ATK ns.varTable.put(tail, var); ns.varTable.Add( tail, var ); // There is no hPtr member in Jacl, The hPtr combines the table // and the key used in a table lookup. var.hashKey = tail; var.table = ns.varTable; var.ns = ns; } } else { // Skip Compiled Local stuff var = null; if ( var == null ) { // look in frame's local var hashtable table = varFrame.varTable; if ( table == null ) { table = new Hashtable(); varFrame.varTable = table; } var = (Var)table[myName]; if ( var == null ) { // we are adding a new entry newvar = true; var = new Var(); SupportClass.PutElement( table, myName, var ); // There is no hPtr member in Jacl, The hPtr combines the table // and the key used in a table lookup. var.hashKey = myName; var.table = table; var.ns = varFrame.ns; } } } if ( !newvar ) { // The variable already exists. Make sure this variable "var" // isn't the same as "other" (avoid circular links). Also, if // it's not an upvar then it's an error. If it is an upvar, then // just disconnect it from the thing it currently refers to. if ( var == other ) { throw new TclException( interp, "can't upvar from variable to itself" ); } if ( var.isVarLink() ) { Var link = (Var)var.value; if ( link == other ) { return; } link.refCount--; if ( link.isVarUndefined() ) { cleanupVar( link, null ); } } else if ( !var.isVarUndefined() ) { throw new TclException( interp, "variable \"" + myName + "\" already exists" ); } else if ( var.traces != null ) { throw new TclException( interp, "variable \"" + myName + "\" has traces: can't use for upvar" ); } } var.setVarLink(); var.clearVarUndefined(); var.value = other; other.refCount++; return; }
public override void eventuallyDispose() { if ( deleted ) { return; } deleted = true; if ( nestLevel > 0 ) { //-- TODO -- Determine why this is an error throw new TclRuntimeError("dispose() called with active evals"); } // Remove our association with the notifer (if we had one). if ( notifier != null ) { notifier.release(); notifier = null; } // Dismantle everything in the global namespace except for the // "errorInfo" and "errorCode" variables. These might be needed // later on if errors occur while deleting commands. We are careful // to destroy and recreate the "errorInfo" and "errorCode" // variables, in case they had any traces on them. // // Dismantle the namespace here, before we clear the assocData. If any // background errors occur here, they will be deleted below. // FIXME : check impl of TclTeardownNamespace NamespaceCmd.teardownNamespace( globalNs ); // Delete all variables. TclObject errorInfoObj = null, errorCodeObj = null; try { errorInfoObj = getVar( "errorInfo", null, TCL.VarFlag.GLOBAL_ONLY ); } catch ( TclException e ) { // Do nothing when var does not exist. } if ( errorInfoObj != null ) { errorInfoObj.preserve(); } try { errorCodeObj = getVar( "errorCode", null, TCL.VarFlag.GLOBAL_ONLY ); } catch ( TclException e ) { // Do nothing when var does not exist. } if ( errorCodeObj != null ) { errorCodeObj.preserve(); } frame = null; varFrame = null; try { if ( errorInfoObj != null ) { setVar( "errorInfo", null, errorInfoObj, TCL.VarFlag.GLOBAL_ONLY ); errorInfoObj.release(); } if ( errorCodeObj != null ) { setVar( "errorCode", null, errorCodeObj, TCL.VarFlag.GLOBAL_ONLY ); errorCodeObj.release(); } } catch ( TclException e ) { // Ignore it -- same behavior as Tcl 8.0. } // Tear down the math function table. expr = null; // Remove all the assoc data tied to this interp and invoke // deletion callbacks; note that a callback can create new // callbacks, so we iterate. // ATK The java code was somethink strong if ( assocData != null ) { foreach ( AssocData data in assocData.Values ) { data.disposeAssocData( this ); } assocData.Clear(); } // Close any remaining channels for ( IDictionaryEnumerator e = interpChanTable.GetEnumerator(); e.MoveNext(); ) { Object key = e.Key; Channel chan = (Channel)e.Value; try { chan.close(); } catch ( IOException ex ) { // Ignore any IO errors } } // Finish deleting the global namespace. // FIXME : check impl of Tcl_DeleteNamespace NamespaceCmd.deleteNamespace( globalNs ); globalNs = null; // Free up the result *after* deleting variables, since variable // deletion could have transferred ownership of the result string // to Tcl. frame = null; varFrame = null; resolvers = null; resetResult(); }
public void createObjCommand( string cmdName, dxObjCmdProc proc, object clientData, dxCmdDeleteProc deleteProc ) // Command object to associate with cmdName. { ImportRef oldRef = null; NamespaceCmd.Namespace ns; WrappedCommand cmd, refCmd; string tail; ImportedCmdData data; int _new; if ( deleted ) { // The interpreter is being deleted. Don't create any new // commands; it's not safe to muck with the interpreter anymore. return; } // Determine where the command should reside. If its name contains // namespace qualifiers, we put it in the specified namespace; // otherwise, we always put it in the global namespace. if ( cmdName.IndexOf( "::" ) != -1 ) { // Java does not support passing an address so we pass // an array of size 1 and then assign arr[0] to the value NamespaceCmd.Namespace[] nsArr = new NamespaceCmd.Namespace[1]; NamespaceCmd.Namespace[] dummyArr = new NamespaceCmd.Namespace[1]; string[] tailArr = new string[1]; NamespaceCmd.getNamespaceForQualName( this, cmdName, null, TCL.VarFlag.CREATE_NS_IF_UNKNOWN, nsArr, dummyArr, dummyArr, tailArr ); ns = nsArr[0]; tail = tailArr[0]; if ( ( ns == null ) || ( (System.Object)tail == null ) ) { return; } } else { ns = globalNs; tail = cmdName; } cmd = (WrappedCommand)ns.cmdTable[tail]; if ( cmd != null ) { /* * Command already exists. If its object-based Tcl_ObjCmdProc is * TclInvokeStringCommand, we just set its Tcl_ObjCmdProc to the * argument "proc". Otherwise, we delete the old command. */ if ( cmd.objProc != null && cmd.objProc.GetType().Name == "TclInvokeStringCommand" ) { cmd.objProc = proc; cmd.objClientData = clientData; cmd.deleteProc = deleteProc; cmd.deleteData = clientData; return; } /* * Otherwise, we delete the old command. Be careful to preserve * any existing import links so we can restore them down below. * That way, you can redefine a command and its import status * will remain intact. */ oldRef = cmd.importRef; cmd.importRef = null; deleteCommandFromToken( cmd ); // FIXME : create a test case for this condition! cmd = (WrappedCommand)ns.cmdTable[tail]; if ( cmd != null ) { // If the deletion callback recreated the command, just throw // away the new command (if we try to delete it again, we // could get stuck in an infinite loop). SupportClass.HashtableRemove( cmd.table, cmd.hashKey ); } } cmd = new WrappedCommand(); ns.cmdTable.Add( tail, cmd ); cmd.table = ns.cmdTable; cmd.hashKey = tail; cmd.ns = ns; cmd.cmd = null; cmd.deleted = false; // FIXME : import feature not implemented //cmd.importRef = null; // TODO -- Determine if this is all correct cmd.objProc = proc; cmd.objClientData = clientData; //cmd.proc = TclInvokeObjectCommand; cmd.clientData = (object)cmd; cmd.deleteProc = deleteProc; cmd.deleteData = clientData; cmd.flags = 0; // Plug in any existing import references found above. Be sure // to update all of these references to point to the new command. if ( oldRef != null ) { cmd.importRef = oldRef; while ( oldRef != null ) { refCmd = oldRef.importedCmd; data = (ImportedCmdData)refCmd.cmd; data.realCmd = cmd; oldRef = oldRef.next; } } // There are no shadowed commands in Jacl because they are only // used in the 8.0 compiler return; }
protected internal void renameCommand( string oldName, string newName ) { Interp interp = this; string newTail; NamespaceCmd.Namespace cmdNs, newNs; WrappedCommand cmd; Hashtable table, oldTable; string hashKey, oldHashKey; // Find the existing command. An error is returned if cmdName can't // be found. cmd = NamespaceCmd.findCommand( interp, oldName, null, 0 ); if ( cmd == null ) { throw new TclException( interp, "can't " + ( ( ( (System.Object)newName == null ) || ( newName.Length == 0 ) ) ? "delete" : "rename" ) + " \"" + oldName + "\": command doesn't exist" ); } cmdNs = cmd.ns; // If the new command name is NULL or empty, delete the command. Do this // with Tcl_DeleteCommandFromToken, since we already have the command. if ( ( (System.Object)newName == null ) || ( newName.Length == 0 ) ) { deleteCommandFromToken( cmd ); return; } // Make sure that the destination command does not already exist. // The rename operation is like creating a command, so we should // automatically create the containing namespaces just like // Tcl_CreateCommand would. NamespaceCmd.Namespace[] newNsArr = new NamespaceCmd.Namespace[1]; NamespaceCmd.Namespace[] dummyArr = new NamespaceCmd.Namespace[1]; string[] newTailArr = new string[1]; NamespaceCmd.getNamespaceForQualName( interp, newName, null, TCL.VarFlag.CREATE_NS_IF_UNKNOWN, newNsArr, dummyArr, dummyArr, newTailArr ); newNs = newNsArr[0]; newTail = newTailArr[0]; if ( ( newNs == null ) || ( (System.Object)newTail == null ) ) { throw new TclException( interp, "can't rename to \"" + newName + "\": bad command name" ); } if ( newNs.cmdTable[newTail] != null ) { throw new TclException( interp, "can't rename to \"" + newName + "\": command already exists" ); } // Warning: any changes done in the code here are likely // to be needed in Tcl_HideCommand() code too. // (until the common parts are extracted out) --dl // Put the command in the new namespace so we can check for an alias // loop. Since we are adding a new command to a namespace, we must // handle any shadowing of the global commands that this might create. oldTable = cmd.table; oldHashKey = cmd.hashKey; newNs.cmdTable.Add( newTail, cmd ); cmd.table = newNs.cmdTable; cmd.hashKey = newTail; cmd.ns = newNs; // FIXME : this is a nasty hack that fixes renaming for Procedures // that move from one namespace to another, but the real problem // is that a rename does not work for Command instances in general if ( cmd.cmd is Procedure ) { Procedure p = (Procedure)cmd.cmd; p.ns = cmd.ns; } // Now check for an alias loop. If we detect one, put everything back // the way it was and report the error. try { interp.preventAliasLoop( interp, cmd ); } catch ( TclException e ) { newNs.cmdTable.Remove( newTail ); cmd.table = oldTable; cmd.hashKey = oldHashKey; cmd.ns = cmdNs; throw; } // The new command name is okay, so remove the command from its // current namespace. This is like deleting the command, so bump // the cmdEpoch to invalidate any cached references to the command. SupportClass.HashtableRemove( oldTable, oldHashKey ); return; }
public Interp() { InitBlock(); //freeProc = null; errorLine = 0; // An empty result is used pretty often. We will use a shared // TclObject instance to represent the empty result so that we // don't need to create a new TclObject instance every time the // interpreter result is set to empty. m_nullResult = TclString.newInstance( "" ); m_nullResult.preserve(); // Increment refCount to 1 m_nullResult.preserve(); // Increment refCount to 2 (shared) m_result = TclString.newInstance( "" ); //m_nullResult; // correcponds to iPtr->objResultPtr m_result.preserve(); expr = new Expression(); nestLevel = 0; maxNestingDepth = 1000; frame = null; varFrame = null; returnCode = TCL.CompletionCode.OK; errorInfo = null; errorCode = null; packageTable = new Hashtable(); packageUnknown = null; cmdCount = 0; termOffset = 0; resolvers = null; evalFlags = 0; scriptFile = null; flags = 0; isSafe = false; assocData = null; globalNs = null; // force creation of global ns below globalNs = NamespaceCmd.createNamespace( this, null, null ); if ( globalNs == null ) { throw new TclRuntimeError( "Interp(): can't create global namespace" ); } // Init things that are specific to the Jacl implementation workingDir = new FileInfo( System.Environment.CurrentDirectory ); noEval = 0; notifier = Notifier.getNotifierForThread( System.Threading.Thread.CurrentThread ); notifier.preserve(); randSeedInit = false; deleted = false; errInProgress = false; errAlreadyLogged = false; errCodeSet = false; dbg = initDebugInfo(); slaveTable = new Hashtable(); targetTable = new Hashtable(); aliasTable = new Hashtable(); // init parser variables Parser.init( this ); TclParse.init( this ); // Initialize the Global (static) channel table and the local // interp channel table. interpChanTable = TclIO.getInterpChanTable( this ); // Sets up the variable trace for tcl_precision. Util.setupPrecisionTrace( this ); // Create the built-in commands. createCommands(); try { // Set up tcl_platform, tcl_version, tcl_library and other // global variables. setVar( "tcl_platform", "platform", "windows", TCL.VarFlag.GLOBAL_ONLY ); setVar( "tcl_platform", "byteOrder", "bigEndian", TCL.VarFlag.GLOBAL_ONLY ); setVar( "tcl_platform", "os", Environment.OSVersion.Platform.ToString(), TCL.VarFlag.GLOBAL_ONLY ); setVar( "tcl_platform", "osVersion", Environment.OSVersion.Version.ToString(), TCL.VarFlag.GLOBAL_ONLY ); setVar( "tcl_platform", "machine", Util.tryGetSystemProperty( "os.arch", "?" ), TCL.VarFlag.GLOBAL_ONLY ); setVar( "tcl_version", TCL_VERSION, TCL.VarFlag.GLOBAL_ONLY ); setVar( "tcl_patchLevel", TCL_PATCH_LEVEL, TCL.VarFlag.GLOBAL_ONLY ); setVar( "tcl_library", "resource:/tcl/lang/library", TCL.VarFlag.GLOBAL_ONLY ); if ( Util.Windows ) { setVar( "tcl_platform", "host_platform", "windows", TCL.VarFlag.GLOBAL_ONLY ); } else if ( Util.Mac ) { setVar( "tcl_platform", "host_platform", "macintosh", TCL.VarFlag.GLOBAL_ONLY ); } else { setVar( "tcl_platform", "host_platform", "unix", TCL.VarFlag.GLOBAL_ONLY ); } // Create the env array an populated it with proper // values. Env.initialize( this ); // Register Tcl's version number. Note: This MUST be // done before the call to evalResource, otherwise // calls to "package require tcl" will fail. pkgProvide( "Tcl", TCL_VERSION ); // Source the init.tcl script to initialize auto-loading. evalResource( "/tcl/lang/library/init.tcl" ); } catch ( TclException e ) { System.Diagnostics.Debug.WriteLine( getResult().ToString() ); SupportClass.WriteStackTrace( e, Console.Error ); throw new TclRuntimeError( "unexpected TclException: " + e.Message, e ); } }
/* *---------------------------------------------------------------------- * * InfoCommandsCmd -- * * Called to implement the "info commands" command that returns the * list of commands in the interpreter that match an optional pattern. * The pattern, if any, consists of an optional sequence of namespace * names separated by "::" qualifiers, which is followed by a * glob-style pattern that restricts which commands are returned. * Handles the following syntax: * * info commands ?pattern? * * Results: * Returns if successful, raises TclException otherwise. * * Side effects: * Returns a result in the interpreter's result object. * *---------------------------------------------------------------------- */ private static void InfoCommandsCmd( Interp interp, TclObject[] objv ) { string cmdName, pattern, simplePattern; IDictionaryEnumerator search; NamespaceCmd.Namespace ns; NamespaceCmd.Namespace globalNs = NamespaceCmd.getGlobalNamespace( interp ); NamespaceCmd.Namespace currNs = NamespaceCmd.getCurrentNamespace( interp ); TclObject list, elemObj; bool specificNsInPattern = false; // Init. to avoid compiler warning. WrappedCommand cmd; // Get the pattern and find the "effective namespace" in which to // list commands. if ( objv.Length == 2 ) { simplePattern = null; ns = currNs; specificNsInPattern = false; } else if ( objv.Length == 3 ) { // From the pattern, get the effective namespace and the simple // pattern (no namespace qualifiers or ::'s) at the end. If an // error was found while parsing the pattern, return it. Otherwise, // if the namespace wasn't found, just leave ns NULL: we will // return an empty list since no commands there can be found. pattern = objv[2].ToString(); // Java does not support passing an address so we pass // an array of size 1 and then assign arr[0] to the value NamespaceCmd.Namespace[] nsArr = new NamespaceCmd.Namespace[1]; NamespaceCmd.Namespace[] dummy1Arr = new NamespaceCmd.Namespace[1]; NamespaceCmd.Namespace[] dummy2Arr = new NamespaceCmd.Namespace[1]; string[] simplePatternArr = new string[1]; NamespaceCmd.getNamespaceForQualName( interp, pattern, null, 0, nsArr, dummy1Arr, dummy2Arr, simplePatternArr ); // Get the values out of the arrays! ns = nsArr[0]; simplePattern = simplePatternArr[0]; if ( ns != null ) { // we successfully found the pattern's ns specificNsInPattern = ( simplePattern.CompareTo( pattern ) != 0 ); } } else { throw new TclNumArgsException( interp, 2, objv, "?pattern?" ); } // Scan through the effective namespace's command table and create a // list with all commands that match the pattern. If a specific // namespace was requested in the pattern, qualify the command names // with the namespace name. list = TclList.newInstance(); if ( ns != null ) { search = ns.cmdTable.GetEnumerator(); while ( search.MoveNext() ) { cmdName = ( (string)search.Key ); if ( ( (System.Object)simplePattern == null ) || Util.stringMatch( cmdName, simplePattern ) ) { if ( specificNsInPattern ) { cmd = (WrappedCommand)search.Value; elemObj = TclString.newInstance( interp.getCommandFullName( cmd ) ); } else { elemObj = TclString.newInstance( cmdName ); } TclList.append( interp, list, elemObj ); } } // If the effective namespace isn't the global :: namespace, and a // specific namespace wasn't requested in the pattern, then add in // all global :: commands that match the simple pattern. Of course, // we add in only those commands that aren't hidden by a command in // the effective namespace. if ( ( ns != globalNs ) && !specificNsInPattern ) { search = globalNs.cmdTable.GetEnumerator(); while ( search.MoveNext() ) { cmdName = ( (string)search.Key ); if ( ( (System.Object)simplePattern == null ) || Util.stringMatch( cmdName, simplePattern ) ) { if ( ns.cmdTable[cmdName] == null ) { TclList.append( interp, list, TclString.newInstance( cmdName ) ); } } } } } interp.setResult( list ); return; }
/* *---------------------------------------------------------------------- * * InfoVarsCmd -- * * Called to implement the "info vars" command that returns the * list of variables in the interpreter that match an optional pattern. * The pattern, if any, consists of an optional sequence of namespace * names separated by "::" qualifiers, which is followed by a * glob-style pattern that restricts which variables are returned. * Handles the following syntax: * * info vars ?pattern? * * Results: * Returns if successful, raises TclException otherwise. * * Side effects: * Returns a result in the interpreter's result object. * *---------------------------------------------------------------------- */ private static void InfoVarsCmd( Interp interp, TclObject[] objv ) { string varName, pattern, simplePattern; IDictionaryEnumerator search; Var var; NamespaceCmd.Namespace ns; NamespaceCmd.Namespace globalNs = NamespaceCmd.getGlobalNamespace( interp ); NamespaceCmd.Namespace currNs = NamespaceCmd.getCurrentNamespace( interp ); TclObject list, elemObj; bool specificNsInPattern = false; // Init. to avoid compiler warning. // Get the pattern and find the "effective namespace" in which to // list variables. We only use this effective namespace if there's // no active Tcl procedure frame. if ( objv.Length == 2 ) { simplePattern = null; ns = currNs; specificNsInPattern = false; } else if ( objv.Length == 3 ) { // From the pattern, get the effective namespace and the simple // pattern (no namespace qualifiers or ::'s) at the end. If an // error was found while parsing the pattern, return it. Otherwise, // if the namespace wasn't found, just leave ns = null: we will // return an empty list since no variables there can be found. pattern = objv[2].ToString(); // Java does not support passing an address so we pass // an array of size 1 and then assign arr[0] to the value NamespaceCmd.Namespace[] nsArr = new NamespaceCmd.Namespace[1]; NamespaceCmd.Namespace[] dummy1Arr = new NamespaceCmd.Namespace[1]; NamespaceCmd.Namespace[] dummy2Arr = new NamespaceCmd.Namespace[1]; string[] simplePatternArr = new string[1]; NamespaceCmd.getNamespaceForQualName( interp, pattern, null, 0, nsArr, dummy1Arr, dummy2Arr, simplePatternArr ); // Get the values out of the arrays! ns = nsArr[0]; simplePattern = simplePatternArr[0]; if ( ns != null ) { // we successfully found the pattern's ns specificNsInPattern = ( simplePattern.CompareTo( pattern ) != 0 ); } } else { throw new TclNumArgsException( interp, 2, objv, "?pattern?" ); } // If the namespace specified in the pattern wasn't found, just return. if ( ns == null ) { return; } list = TclList.newInstance(); if ( ( interp.varFrame == null ) || !interp.varFrame.isProcCallFrame || specificNsInPattern ) { // There is no frame pointer, the frame pointer was pushed only // to activate a namespace, or we are in a procedure call frame // but a specific namespace was specified. Create a list containing // only the variables in the effective namespace's variable table. search = ns.varTable.GetEnumerator(); while ( search.MoveNext() ) { varName = ( (string)search.Key ); var = (Var)search.Value; if ( !var.isVarUndefined() || ( ( var.flags & VarFlags.NAMESPACE_VAR ) != 0 ) ) { if ( ( (System.Object)simplePattern == null ) || Util.stringMatch( varName, simplePattern ) ) { if ( specificNsInPattern ) { elemObj = TclString.newInstance( Var.getVariableFullName( interp, var ) ); } else { elemObj = TclString.newInstance( varName ); } TclList.append( interp, list, elemObj ); } } } // If the effective namespace isn't the global :: namespace, and a // specific namespace wasn't requested in the pattern (i.e., the // pattern only specifies variable names), then add in all global :: // variables that match the simple pattern. Of course, add in only // those variables that aren't hidden by a variable in the effective // namespace. if ( ( ns != globalNs ) && !specificNsInPattern ) { search = globalNs.varTable.GetEnumerator(); while ( search.MoveNext() ) { varName = ( (string)search.Key ); var = (Var)search.Value; if ( !var.isVarUndefined() || ( ( var.flags & VarFlags.NAMESPACE_VAR ) != 0 ) ) { if ( ( (System.Object)simplePattern == null ) || Util.stringMatch( varName, simplePattern ) ) { // Skip vars defined in current namespace if ( ns.varTable[varName] == null ) { TclList.append( interp, list, TclString.newInstance( varName ) ); } } } } } } else { AppendLocals( interp, list, simplePattern, true ); } interp.setResult( list ); return; }
/// <summary> /// Tcl_ProcObjCmd -> ProcCmd.cmdProc /// /// Creates a new Tcl procedure. /// /// </summary> /// <param name="interp">the current interpreter. /// </param> /// <param name="objv">command arguments. /// </param> /// <exception cref=""> TclException If incorrect number of arguments. /// </exception> public TCL.CompletionCode cmdProc(Interp interp, TclObject[] objv) { Procedure proc; string fullName, procName; NamespaceCmd.Namespace ns, altNs, cxtNs; Command cmd; System.Text.StringBuilder ds; if (objv.Length != 4) { throw new TclNumArgsException(interp, 1, objv, "name args body"); } // Determine the namespace where the procedure should reside. Unless // the command name includes namespace qualifiers, this will be the // current namespace. fullName = objv[1].ToString(); // Java does not support passing an address so we pass // an array of size 1 and then assign arr[0] to the value NamespaceCmd.Namespace[] nsArr = new NamespaceCmd.Namespace[1]; NamespaceCmd.Namespace[] altNsArr = new NamespaceCmd.Namespace[1]; NamespaceCmd.Namespace[] cxtNsArr = new NamespaceCmd.Namespace[1]; string[] procNameArr = new string[1]; NamespaceCmd.getNamespaceForQualName(interp, fullName, null, 0, nsArr, altNsArr, cxtNsArr, procNameArr); // Get the values out of the arrays ns = nsArr[0]; altNs = altNsArr[0]; cxtNs = cxtNsArr[0]; procName = procNameArr[0]; if (ns == null) { throw new TclException(interp, "can't create procedure \"" + fullName + "\": unknown namespace"); } if ((System.Object) procName == null) { throw new TclException(interp, "can't create procedure \"" + fullName + "\": bad procedure name"); } // FIXME : could there be a problem with a command named ":command" ? if ((ns != NamespaceCmd.getGlobalNamespace(interp)) && ((System.Object) procName != null) && ((procName.Length > 0) && (procName[0] == ':'))) { throw new TclException(interp, "can't create procedure \"" + procName + "\" in non-global namespace with name starting with \":\""); } // Create the data structure to represent the procedure. proc = new Procedure(interp, ns, procName, objv[2], objv[3], interp.ScriptFile, interp.getArgLineNumber(3)); // Now create a command for the procedure. This will initially be in // the current namespace unless the procedure's name included namespace // qualifiers. To create the new command in the right namespace, we // generate a fully qualified name for it. ds = new System.Text.StringBuilder(); if (ns != NamespaceCmd.getGlobalNamespace(interp)) { ds.Append(ns.fullName); ds.Append("::"); } ds.Append(procName); interp.createCommand(ds.ToString(), proc); // Now initialize the new procedure's cmdPtr field. This will be used // later when the procedure is called to determine what namespace the // procedure will run in. This will be different than the current // namespace if the proc was renamed into a different namespace. // FIXME : we do not handle renaming into another namespace correctly yet! //procPtr->cmdPtr = (Command *) cmd; return TCL.CompletionCode.RETURN; }
/// <summary> Chain this frame into the call frame stack and binds the parameters /// values to the formal parameters of the procedure. /// /// </summary> /// <param name="proc">the procedure. /// </param> /// <param name="proc">argv the parameter values. /// </param> /// <exception cref=""> TclException if wrong number of arguments. /// </exception> internal void chain( Procedure proc, TclObject[] objv ) { // FIXME: double check this ns thing in case where proc is renamed to different ns. this.ns = proc.ns; this.objv = objv; // FIXME : quick level hack : fix later level = ( interp.varFrame == null ) ? 1 : ( interp.varFrame.level + 1 ); caller = interp.frame; callerVar = interp.varFrame; interp.frame = this; interp.varFrame = this; // parameter bindings int numArgs = proc.argList.Length; if ( ( !proc.isVarArgs ) && ( objv.Length - 1 > numArgs ) ) { wrongNumProcArgs( objv[0], proc ); } int i, j; for ( i = 0, j = 1; i < numArgs; i++, j++ ) { // Handle the special case of the last formal being // "args". When it occurs, assign it a list consisting of // all the remaining actual arguments. TclObject varName = proc.argList[i][0]; TclObject value = null; if ( ( i == ( numArgs - 1 ) ) && proc.isVarArgs ) { value = TclList.newInstance(); value.preserve(); for ( int k = j; k < objv.Length; k++ ) { TclList.append( interp, value, objv[k] ); } interp.setVar( varName, value, 0 ); value.release(); } else { if ( j < objv.Length ) { value = objv[j]; } else if ( proc.argList[i][1] != null ) { value = proc.argList[i][1]; } else { wrongNumProcArgs( objv[0], proc ); } interp.setVar( varName, value, 0 ); } } }
/// <summary> Creates a CallFrame for the global variables.</summary> /// <param name="interp">current interpreter. /// </param> internal CallFrame( Interp i ) { interp = i; ns = i.globalNs; varTable = new Hashtable(); caller = null; callerVar = null; objv = null; level = 0; isProcCallFrame = true; }