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;
    }
Exemple #2
0
		/*
		*----------------------------------------------------------------------
		*
		* 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 ;
		}
Exemple #3
0
		/*
		*----------------------------------------------------------------------
		*
		* 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 ;
		}
Exemple #4
0
		/*
		*----------------------------------------------------------------------
		*
		* 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 ;
		}