wasCSharpSQLite – Rev 1

Subversion Repositories:
Rev:
#undef DEBUG
/*
* Procedure.java --
*
*       This class implements the body of a Tcl procedure.
*
* Copyright (c) 1997 Cornell University.
* Copyright (c) 1997 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
* 
* Included in SQLite3 port to C# for use in testharness only;  2008 Noah B Hart
*
* RCS @(#) $Id: Procedure.java,v 1.4 1999/08/05 03:40:31 mo Exp $
*
*/
using System;
namespace tcl.lang
{

  /// <summary> This class implements the body of a Tcl procedure.</summary>

  public class Procedure : Command, CommandWithDispose
  {

    // The formal parameters of the procedure and their default values.
    //     argList[0][0] = name of the 1st formal param
    //     argList[0][1] = if non-null, default value of the 1st formal param


    internal TclObject[][] argList;

    // True if this proc takes an variable number of arguments. False
    // otherwise.

    internal bool isVarArgs;

    // The body of the procedure.

    internal CharPointer body;
    internal int body_length;

    // The namespace that the Command is defined in
    internal NamespaceCmd.Namespace ns;

    // Name of the source file that contains this procedure. May be null, which
    // indicates that the source file is unknown.

    internal string srcFileName;

    // Position where the body of the procedure starts in the source file.
    // 1 means the first line in the source file.

    internal int srcLineNumber;

    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();
    }
    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;
    }
    public void disposeCmd()
    {
      //body.release();
      body = null;
      for ( int i = 0; i < argList.Length; i++ )
      {
        argList[i][0].release();
        argList[i][0] = null;

        if ( argList[i][1] != null )
        {
          argList[i][1].release();
          argList[i][1] = null;
        }
      }
      argList = null;
    }

    internal static bool isProc( WrappedCommand cmd )
    {
      return ( cmd.cmd is Procedure );

      /*
      // FIXME: do we really want to get the original command
      // and test that? Methods like InfoCmd.InfoProcsCmd seem
      // to do this already.
                        
      WrappedCommand origCmd;
                        
      origCmd = NamespaceCmd.getOriginalCommand(cmd);
      if (origCmd != null) {
      cmd = origCmd;
      }
      return (cmd.cmd instanceof Procedure);
      */
    }

    internal static Procedure findProc( Interp interp, string procName )
    {
      WrappedCommand cmd;
      WrappedCommand origCmd;

      try
      {
        cmd = NamespaceCmd.findCommand( interp, procName, null, 0 );
      }
      catch ( TclException e )
      {
        // This should never happen
        throw new TclRuntimeError( "unexpected TclException: " + e.Message );
      }

      if ( cmd == null )
      {
        return null;
      }

      origCmd = NamespaceCmd.getOriginalCommand( cmd );
      if ( origCmd != null )
      {
        cmd = origCmd;
      }
      if ( !( cmd.cmd is Procedure ) )
      {
        return null;
      }
      return (Procedure)cmd.cmd;
    }
  } // end Procedure
}