wasCSharpSQLite – Rev 1
?pathlinks?
/*
* CallFrame.java
*
* Copyright (c) 1997 Cornell University.
* Copyright (c) 1997-1998 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: CallFrame.java,v 1.10 2003/01/08 02:10:17 mdejong Exp $
*
*/
using System.Collections;
using System.Text;
namespace tcl.lang
{
/// <summary> This class implements a frame in the call stack.
///
/// This class can be overridden to define new variable scoping rules for
/// the Tcl interpreter.
/// </summary>
public class CallFrame
{
internal ArrayList VarNames
{
// FIXME : need to port Tcl 8.1 implementation here
get
{
ArrayList vector = new ArrayList( 10 );
if ( varTable == null )
{
return vector;
}
for ( IEnumerator e1 = varTable.Values.GetEnumerator(); e1.MoveNext(); )
{
Var v = (Var)e1.Current;
if ( !v.isVarUndefined() )
{
vector.Add( v.hashKey );
}
}
return vector;
}
}
/// <returns> an Vector the names of the (defined) local variables
/// in this CallFrame (excluding upvar's)
/// </returns>
internal ArrayList LocalVarNames
{
get
{
ArrayList vector = new ArrayList( 10 );
if ( varTable == null )
{
return vector;
}
for ( IEnumerator e1 = varTable.Values.GetEnumerator(); e1.MoveNext(); )
{
Var v = (Var)e1.Current;
if ( !v.isVarUndefined() && !v.isVarLink() )
{
vector.Add( v.hashKey );
}
}
return vector;
}
}
/// <summary> The interpreter associated with this call frame.</summary>
protected internal Interp interp;
/// <summary> The Namespace this CallFrame is executing in.
/// Used to resolve commands and global variables.
/// </summary>
internal NamespaceCmd.Namespace ns;
/// <summary> If true, the frame was pushed to execute a Tcl procedure
/// and may have local vars. If false, the frame was pushed to execute
/// a namespace command and var references are treated as references
/// to namespace vars; varTable is ignored.
/// </summary>
internal bool isProcCallFrame;
/// <summary> Stores the arguments of the procedure associated with this CallFrame.
/// Is null for global level.
/// </summary>
internal TclObject[] objv;
/// <summary> Value of interp.frame when this procedure was invoked
/// (i.e. next in stack of all active procedures).
/// </summary>
protected internal CallFrame caller;
/// <summary> Value of interp.varFrame when this procedure was invoked
/// (i.e. determines variable scoping within caller; same as
/// caller unless an "uplevel" command or something equivalent
/// was active in the caller).
/// </summary>
protected internal CallFrame callerVar;
/// <summary> Level of recursion. = 0 for the global level.</summary>
protected internal int level;
/// <summary> Stores the variables of this CallFrame.</summary>
protected internal Hashtable varTable;
/// <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;
}
/// <summary> Creates a CallFrame. It changes the following variables:
///
/// <ul>
/// <li> this.caller
/// <li> this.callerVar
/// <li> interp.frame
/// <li> interp.varFrame
/// </ul>
/// </summary>
/// <param name="i">current interpreter.
/// </param>
/// <param name="proc">the procedure to invoke in this call frame.
/// </param>
/// <param name="objv">the arguments to the procedure.
/// </param>
/// <exception cref=""> TclException if error occurs in parameter bindings.
/// </exception>
internal CallFrame( Interp i, Procedure proc, TclObject[] objv )
: this( i )
{
try
{
chain( proc, objv );
}
catch ( TclException e )
{
dispose();
throw;
}
}
/// <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 );
}
}
}
private string wrongNumProcArgs( TclObject name, Procedure proc )
{
int i;
StringBuilder sbuf = new StringBuilder( 200 );
sbuf.Append( "wrong # args: should be \"" );
sbuf.Append( name.ToString() );
for ( i = 0; i < proc.argList.Length; i++ )
{
TclObject arg = proc.argList[i][0];
TclObject def = proc.argList[i][1];
sbuf.Append( " " );
if ( def != null )
sbuf.Append( "?" );
sbuf.Append( arg.ToString() );
if ( def != null )
sbuf.Append( "?" );
}
sbuf.Append( "\"" );
throw new TclException( interp, sbuf.ToString() );
}
/// <param name="name">the name of the variable.
///
/// </param>
/// <returns> true if a variable exists and is defined inside this
/// CallFrame, false otherwise
/// </returns>
internal static bool exists( Interp interp, string name )
{
try
{
Var[] result = Var.lookupVar( interp, name, null, 0, "lookup", false, false );
if ( result == null )
{
return false;
}
if ( result[0].isVarUndefined() )
{
return false;
}
return true;
}
catch ( TclException e )
{
throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
}
}
/// <returns> an Vector the names of the (defined) variables
/// in this CallFrame.
/// </returns>
/// <summary> Tcl_GetFrame -> getFrame
///
/// Given a description of a procedure frame, such as the first
/// argument to an "uplevel" or "upvar" command, locate the
/// call frame for the appropriate level of procedure.
///
/// The return value is 1 if string was either a number or a number
/// preceded by "#" and it specified a valid frame. 0 is returned
/// if string isn't one of the two things above (in this case,
/// the lookup acts as if string were "1"). The frameArr[0] reference
/// will be filled by the reference of the desired frame (unless an
/// error occurs, in which case it isn't modified).
///
/// </summary>
/// <param name="string">a string that specifies the level.
/// </param>
/// <exception cref=""> TclException if s is a valid level specifier but
/// refers to a bad level that doesn't exist.
/// </exception>
internal static int getFrame( Interp interp, string inString, CallFrame[] frameArr )
{
int curLevel, level, result;
CallFrame frame;
// Parse string to figure out which level number to go to.
result = 1;
curLevel = ( interp.varFrame == null ) ? 0 : interp.varFrame.level;
if ( ( inString.Length > 0 ) && ( inString[0] == '#' ) )
{
level = Util.getInt( interp, inString.Substring( 1 ) );
if ( level < 0 )
{
throw new TclException( interp, "bad level \"" + inString + "\"" );
}
}
else if ( ( inString.Length > 0 ) && System.Char.IsDigit( inString[0] ) )
{
level = Util.getInt( interp, inString );
level = curLevel - level;
}
else
{
level = curLevel - 1;
result = 0;
}
// FIXME: is this a bad comment from some other proc?
// Figure out which frame to use, and modify the interpreter so
// its variables come from that frame.
if ( level == 0 )
{
frame = null;
}
else
{
for ( frame = interp.varFrame; frame != null; frame = frame.callerVar )
{
if ( frame.level == level )
{
break;
}
}
if ( frame == null )
{
throw new TclException( interp, "bad level \"" + inString + "\"" );
}
}
frameArr[0] = frame;
return result;
}
/// <summary> This method is called when this CallFrame is no longer needed.
/// Removes the reference of this object from the interpreter so
/// that this object can be garbage collected.
/// <p>
/// For this procedure to work correctly, it must not be possible
/// for any of the variable in the table to be accessed from Tcl
/// commands (e.g. from trace procedures).
/// </summary>
protected internal void dispose()
{
// Unchain this frame from the call stack.
interp.frame = caller;
interp.varFrame = callerVar;
caller = null;
callerVar = null;
if ( varTable != null )
{
Var.deleteVars( interp, varTable );
varTable.Clear();
varTable = null;
}
}
}
}