public TCL.CompletionCode cmdProc( Interp interp, TclObject[] argv ) { // Create the call frame and parameter bindings CallFrame frame = interp.newCallFrame( this, argv ); // Execute the body interp.pushDebugStack( srcFileName, srcLineNumber ); try { Parser.eval2( interp, body.array, body.index, body_length, 0 ); } catch ( TclException e ) { TCL.CompletionCode code = e.getCompletionCode(); if ( code == TCL.CompletionCode.RETURN ) { TCL.CompletionCode realCode = interp.updateReturnInfo(); if ( realCode != TCL.CompletionCode.OK ) { e.setCompletionCode( realCode ); throw; } } else if ( code == TCL.CompletionCode.ERROR ) { interp.addErrorInfo( "\n (procedure \"" + argv[0] + "\" line " + interp.errorLine + ")" ); throw; } else if ( code == TCL.CompletionCode.BREAK ) { throw new TclException( interp, "invoked \"break\" outside of a loop" ); } else if ( code == TCL.CompletionCode.CONTINUE ) { throw new TclException( interp, "invoked \"continue\" outside of a loop" ); } else { throw; } } finally { interp.popDebugStack(); // The check below is a hack. The problem is that there // could be unset traces on the variables, which cause // scripts to be evaluated. This will clear the // errInProgress flag, losing stack trace information if // the procedure was exiting with an error. The code // below preserves the flag. Unfortunately, that isn't // really enough: we really should preserve the errorInfo // variable too (otherwise a nested error in the trace // script will trash errorInfo). What's really needed is // a general-purpose mechanism for saving and restoring // interpreter state. if ( interp.errInProgress ) { frame.dispose(); interp.errInProgress = true; } else { frame.dispose(); } } return TCL.CompletionCode.RETURN; }
/* *---------------------------------------------------------------------- * * NamespaceEvalCmd -> evalCmd * * Invoked to implement the "namespace eval" command. Executes * commands in a namespace. If the namespace does not already exist, * it is created. Handles the following syntax: * * namespace eval name arg ?arg...? * * If more than one arg argument is specified, the command that is * executed is the result of concatenating the arguments together with * a space between each argument. * * Results: * Returns if successful, raises TclException if something goes wrong. * * Side effects: * Returns the result of the command in the interpreter's result * object. If anything goes wrong, this procedure returns an error * message as the result. * *---------------------------------------------------------------------- */ private static void evalCmd(Interp interp, TclObject[] objv) { Namespace namespace_Renamed; CallFrame frame; string cmd; string name; int length; if (objv.Length < 4) { throw new TclNumArgsException(interp, 2, objv, "name arg ?arg...?"); } // Try to resolve the namespace reference, caching the result in the // namespace object along the way. namespace_Renamed = getNamespaceFromObj(interp, objv[2]); // If the namespace wasn't found, try to create it. if (namespace_Renamed == null) { name = objv[2].ToString(); namespace_Renamed = createNamespace(interp, name, null); if (namespace_Renamed == null) { // FIXME : result hack, we get the interp result and throw it! throw new TclException(interp, interp.getResult().ToString()); } } // Make the specified namespace the current namespace and evaluate // the command(s). frame = interp.newCallFrame(); pushCallFrame(interp, frame, namespace_Renamed, false); try { if (objv.Length == 4) { interp.eval(objv[3], 0); } else { cmd = Util.concat(3, objv.Length, objv); // eval() will delete the object when it decrements its // refcount after eval'ing it. interp.eval(cmd); // do not pass TCL_EVAL_DIRECT, for compiler only } } catch (TclException ex) { if (ex.getCompletionCode() == TCL.CompletionCode.ERROR) { interp.addErrorInfo("\n (in namespace eval \"" + namespace_Renamed.fullName + "\" script line " + interp.errorLine + ")"); } throw ex; } finally { popCallFrame(interp); } return ; }
/* *---------------------------------------------------------------------- * * NamespaceInscopeCmd -> inscopeCmd * * Invoked to implement the "namespace inscope" command that executes a * script in the context of a particular namespace. This command is not * expected to be used directly by programmers; calls to it are * generated implicitly when programs use "namespace code" commands * to register callback scripts. Handles the following syntax: * * namespace inscope name arg ?arg...? * * The "namespace inscope" command is much like the "namespace eval" * command except that it has lappend semantics and the namespace must * already exist. It treats the first argument as a list, and appends * any arguments after the first onto the end as proper list elements. * For example, * * namespace inscope ::foo a b c d * * is equivalent to * * namespace eval ::foo [concat a [list b c d]] * * This lappend semantics is important because many callback scripts * are actually prefixes. * * Results: * Returns if successful, raises TclException if something goes wrong. * * Side effects: * Returns a result in the Tcl interpreter's result object. * *---------------------------------------------------------------------- */ private static void inscopeCmd(Interp interp, TclObject[] objv) { Namespace namespace_Renamed; CallFrame frame; int i, result; if (objv.Length < 4) { throw new TclNumArgsException(interp, 2, objv, "name arg ?arg...?"); } // Resolve the namespace reference. namespace_Renamed = getNamespaceFromObj(interp, objv[2]); if (namespace_Renamed == null) { throw new TclException(interp, "unknown namespace \"" + objv[2].ToString() + "\" in inscope namespace command"); } // Make the specified namespace the current namespace. frame = interp.newCallFrame(); pushCallFrame(interp, frame, namespace_Renamed, false); // Execute the command. If there is just one argument, just treat it as // a script and evaluate it. Otherwise, create a list from the arguments // after the first one, then concatenate the first argument and the list // of extra arguments to form the command to evaluate. try { if (objv.Length == 4) { interp.eval(objv[3], 0); } else { TclObject[] concatObjv = new TclObject[2]; TclObject list; string cmd; list = TclList.newInstance(); for (i = 4; i < objv.Length; i++) { try { TclList.append(interp, list, objv[i]); } catch (TclException ex) { list.release(); // free unneeded obj throw ex; } } concatObjv[0] = objv[3]; concatObjv[1] = list; cmd = Util.concat(0, 1, concatObjv); interp.eval(cmd); // do not pass TCL_EVAL_DIRECT, for compiler only list.release(); // we're done with the list object } } catch (TclException ex) { if (ex.getCompletionCode() == TCL.CompletionCode.ERROR) { interp.addErrorInfo("\n (in namespace inscope \"" + namespace_Renamed.fullName + "\" script line " + interp.errorLine + ")"); } throw ex; } finally { popCallFrame(interp); } return ; }
/* *---------------------------------------------------------------------- * * TclGetNamespaceForQualName -> getNamespaceForQualName * * Given a qualified name specifying a command, variable, or namespace, * and a namespace in which to resolve the name, this procedure returns * a pointer to the namespace that contains the item. A qualified name * consists of the "simple" name of an item qualified by the names of * an arbitrary number of containing namespace separated by "::"s. If * the qualified name starts with "::", it is interpreted absolutely * from the global namespace. Otherwise, it is interpreted relative to * the namespace specified by cxtNsPtr if it is non-null. If cxtNsPtr * is null, the name is interpreted relative to the current namespace. * * A relative name like "foo::bar::x" can be found starting in either * the current namespace or in the global namespace. So each search * usually follows two tracks, and two possible namespaces are * returned. If the procedure sets either nsPtrPtr[0] or altNsPtrPtr[0] to * null, then that path failed. * * If "flags" contains TCL.VarFlag.GLOBAL_ONLY, the relative qualified name is * sought only in the global :: namespace. The alternate search * (also) starting from the global namespace is ignored and * altNsPtrPtr[0] is set null. * * If "flags" contains TCL.VarFlag.NAMESPACE_ONLY, the relative qualified * name is sought only in the namespace specified by cxtNsPtr. The * alternate search starting from the global namespace is ignored and * altNsPtrPtr[0] is set null. If both TCL.VarFlag.GLOBAL_ONLY and * TCL.VarFlag.NAMESPACE_ONLY are specified, TCL.VarFlag.GLOBAL_ONLY is ignored and * the search starts from the namespace specified by cxtNsPtr. * * If "flags" contains TCL.VarFlag.CREATE_NS_IF_UNKNOWN, all namespace * components of the qualified name that cannot be found are * automatically created within their specified parent. This makes sure * that functions like Tcl_CreateCommand always succeed. There is no * alternate search path, so altNsPtrPtr[0] is set null. * * If "flags" contains TCL.VarFlag.FIND_ONLY_NS, the qualified name is treated as a * reference to a namespace, and the entire qualified name is * followed. If the name is relative, the namespace is looked up only * in the current namespace. A pointer to the namespace is stored in * nsPtrPtr[0] and null is stored in simpleNamePtr[0]. Otherwise, if * TCL.VarFlag.FIND_ONLY_NS is not specified, only the leading components are * treated as namespace names, and a pointer to the simple name of the * final component is stored in simpleNamePtr[0]. * * Results: * It sets nsPtrPtr[0] and altNsPtrPtr[0] to point to the two possible * namespaces which represent the last (containing) namespace in the * qualified name. If the procedure sets either nsPtrPtr[0] or altNsPtrPtr[0] * to null, then the search along that path failed. The procedure also * stores a pointer to the simple name of the final component in * simpleNamePtr[0]. If the qualified name is "::" or was treated as a * namespace reference (TCL.VarFlag.FIND_ONLY_NS), the procedure stores a pointer * to the namespace in nsPtrPtr[0], null in altNsPtrPtr[0], and sets * simpleNamePtr[0] to an empty string. * * If there is an error, this procedure returns TCL_ERROR. If "flags" * contains TCL_LEAVE_ERR_MSG, an error message is returned in the * interpreter's result object. Otherwise, the interpreter's result * object is left unchanged. * * actualCxtPtrPtr[0] is set to the actual context namespace. It is * set to the input context namespace pointer in cxtNsPtr. If cxtNsPtr * is null, it is set to the current namespace context. * * Side effects: * If "flags" contains TCL.VarFlag.CREATE_NS_IF_UNKNOWN, new namespaces may be * created. * *---------------------------------------------------------------------- */ internal static void getNamespaceForQualName(Interp interp, string qualName, Namespace cxtNsPtr, TCL.VarFlag flags, Namespace[] nsPtrPtr, Namespace[] altNsPtrPtr, Namespace[] actualCxtPtrPtr, string[] simpleNamePtr) { // FIXME : remove extra method call checks when we are sure this works! if (true) { // check invariants if ((nsPtrPtr == null) || (nsPtrPtr.Length != 1)) { throw new System.SystemException("nsPtrPtr " + nsPtrPtr); } if ((altNsPtrPtr == null) || (altNsPtrPtr.Length != 1)) { throw new System.SystemException("altNsPtrPtr " + altNsPtrPtr); } if ((actualCxtPtrPtr == null) || (actualCxtPtrPtr.Length != 1)) { throw new System.SystemException("actualCxtPtrPtr " + actualCxtPtrPtr); } if ((simpleNamePtr == null) || (simpleNamePtr.Length != 1)) { throw new System.SystemException("simpleNamePtr " + simpleNamePtr); } } Namespace ns = cxtNsPtr; Namespace altNs; Namespace globalNs = getGlobalNamespace(interp); Namespace entryNs; string start, end; string nsName; int len; int start_ind, end_ind, name_len; // Determine the context namespace ns in which to start the primary // search. If TCL.VarFlag.NAMESPACE_ONLY or TCL.VarFlag.FIND_ONLY_NS was specified, search // from the current namespace. If the qualName name starts with a "::" // or TCL.VarFlag.GLOBAL_ONLY was specified, search from the global // namespace. Otherwise, use the given namespace given in cxtNsPtr, or // if that is null, use the current namespace context. Note that we // always treat two or more adjacent ":"s as a namespace separator. if ((flags & (TCL.VarFlag.NAMESPACE_ONLY | TCL.VarFlag.FIND_ONLY_NS)) != 0) { ns = getCurrentNamespace(interp); } else if ((flags & TCL.VarFlag.GLOBAL_ONLY) != 0) { ns = globalNs; } else if (ns == null) { if (interp.varFrame != null) { ns = interp.varFrame.ns; } else { ns = interp.globalNs; } } start_ind = 0; name_len = qualName.Length; if ((name_len >= 2) && (qualName[0] == ':') && (qualName[1] == ':')) { start_ind = 2; // skip over the initial :: while ((start_ind < name_len) && (qualName[start_ind] == ':')) { start_ind++; // skip over a subsequent : } ns = globalNs; if (start_ind >= name_len) { // qualName is just two or more ":"s nsPtrPtr[0] = globalNs; altNsPtrPtr[0] = null; actualCxtPtrPtr[0] = globalNs; simpleNamePtr[0] = ""; // points to empty string return ; } } actualCxtPtrPtr[0] = ns; // Start an alternate search path starting with the global namespace. // However, if the starting context is the global namespace, or if the // flag is set to search only the namespace cxtNs, ignore the // alternate search path. altNs = globalNs; if ((ns == globalNs) || ((flags & (TCL.VarFlag.NAMESPACE_ONLY | TCL.VarFlag.FIND_ONLY_NS)) != 0)) { altNs = null; } // Loop to resolve each namespace qualifier in qualName. end_ind = start_ind; while (start_ind < name_len) { // Find the next namespace qualifier (i.e., a name ending in "::") // or the end of the qualified name (i.e., a name ending in "\0"). // Set len to the number of characters, starting from start, // in the name; set end to point after the "::"s or at the "\0". len = 0; for (end_ind = start_ind; end_ind < name_len; end_ind++) { if (((name_len - end_ind) > 1) && (qualName[end_ind] == ':') && (qualName[end_ind + 1] == ':')) { end_ind += 2; // skip over the initial :: while ((end_ind < name_len) && (qualName[end_ind] == ':')) { end_ind++; // skip over a subsequent : } break; } len++; } if ((end_ind == name_len) && !((end_ind - start_ind >= 2) && ((qualName[end_ind - 1] == ':') && (qualName[end_ind - 2] == ':')))) { // qualName ended with a simple name at start. If TCL.VarFlag.FIND_ONLY_NS // was specified, look this up as a namespace. Otherwise, // start is the name of a cmd or var and we are done. if ((flags & TCL.VarFlag.FIND_ONLY_NS) != 0) { // assign the string from start_ind to the end of the name string nsName = qualName.Substring(start_ind); } else { nsPtrPtr[0] = ns; altNsPtrPtr[0] = altNs; simpleNamePtr[0] = qualName.Substring(start_ind); return ; } } else { // start points to the beginning of a namespace qualifier ending // in "::". Create new string with the namespace qualifier. nsName = qualName.Substring(start_ind, (start_ind + len) - (start_ind)); } // Look up the namespace qualifier nsName in the current namespace // context. If it isn't found but TCL.VarFlag.CREATE_NS_IF_UNKNOWN is set, // create that qualifying namespace. This is needed for procedures // like Tcl_CreateCommand that cannot fail. if (ns != null) { entryNs = (Namespace) ns.childTable[nsName]; if (entryNs != null) { ns = entryNs; } else if ((flags & TCL.VarFlag.CREATE_NS_IF_UNKNOWN) != 0) { CallFrame frame = interp.newCallFrame(); pushCallFrame(interp, frame, ns, false); ns = createNamespace(interp, nsName, null); popCallFrame(interp); if (ns == null) { throw new System.SystemException("Could not create namespace " + nsName); } } else { ns = null; // namespace not found and wasn't created } } // Look up the namespace qualifier in the alternate search path too. if (altNs != null) { altNs = (Namespace) altNs.childTable[nsName]; } // If both search paths have failed, return null results. if ((ns == null) && (altNs == null)) { nsPtrPtr[0] = null; altNsPtrPtr[0] = null; simpleNamePtr[0] = null; return ; } start_ind = end_ind; } // We ignore trailing "::"s in a namespace name, but in a command or // variable name, trailing "::"s refer to the cmd or var named {}. if (((flags & TCL.VarFlag.FIND_ONLY_NS) != 0) || ((end_ind > start_ind) && (qualName[end_ind - 1] != ':'))) { simpleNamePtr[0] = null; // found namespace name } else { // FIXME : make sure this does not throw exception when end_ind is at the end of the string simpleNamePtr[0] = qualName.Substring(end_ind); // found cmd/var: points to empty string } // As a special case, if we are looking for a namespace and qualName // is "" and the current active namespace (ns) is not the global // namespace, return null (no namespace was found). This is because // namespaces can not have empty names except for the global namespace. if (((flags & TCL.VarFlag.FIND_ONLY_NS) != 0) && (name_len == 0) && (ns != globalNs)) { ns = null; } nsPtrPtr[0] = ns; altNsPtrPtr[0] = altNs; return ; }