PageRenderTime 50ms CodeModel.GetById 42ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-0-pre3/bsh/BshMethod.java

#
Java | 207 lines | 101 code | 20 blank | 86 comment | 20 complexity | 7328a0a0d2c4d6df6c1f79a7707b35d2 MD5 | raw file
  1/*****************************************************************************
  2 *                                                                           *
  3 *  This file is part of the BeanShell Java Scripting distribution.          *
  4 *  Documentation and updates may be found at http://www.beanshell.org/      *
  5 *                                                                           *
  6 *  Sun Public License Notice:                                               *
  7 *                                                                           *
  8 *  The contents of this file are subject to the Sun Public License Version  *
  9 *  1.0 (the "License"); you may not use this file except in compliance with *
 10 *  the License. A copy of the License is available at http://www.sun.com    * 
 11 *                                                                           *
 12 *  The Original Code is BeanShell. The Initial Developer of the Original    *
 13 *  Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright     *
 14 *  (C) 2000.  All Rights Reserved.                                          *
 15 *                                                                           *
 16 *  GNU Public License Notice:                                               *
 17 *                                                                           *
 18 *  Alternatively, the contents of this file may be used under the terms of  *
 19 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
 20 *  provisions of LGPL are applicable instead of those above. If you wish to *
 21 *  allow use of your version of this file only under the  terms of the LGPL *
 22 *  and not to allow others to use your version of this file under the SPL,  *
 23 *  indicate your decision by deleting the provisions above and replace      *
 24 *  them with the notice and other provisions required by the LGPL.  If you  *
 25 *  do not delete the provisions above, a recipient may use your version of  *
 26 *  this file under either the SPL or the LGPL.                              *
 27 *                                                                           *
 28 *  Patrick Niemeyer (pat@pat.net)                                           *
 29 *  Author of Learning Java, O'Reilly & Associates                           *
 30 *  http://www.pat.net/~pat/                                                 *
 31 *                                                                           *
 32 *****************************************************************************/
 33
 34package bsh;
 35
 36/**
 37	This represents an *instance* of a bsh method declaration in a particular
 38	namespace.  This is a thin wrapper around the BSHMethodDeclaration
 39	with a pointer to the declaring namespace.
 40
 41	The issue is that when a method is located in a subordinate namespace or
 42	invoked from an arbitrary namespace it must nontheless execute with its
 43	'super' as the context in which it was declared.
 44
 45	i.e.
 46	The local method context is a child namespace of the declaring namespace.
 47*/
 48class BshMethod implements java.io.Serializable 
 49{
 50	BSHMethodDeclaration method;
 51
 52	/* 
 53		I believe this is always the namespace in which the method is
 54		defined...  It is a back-reference for the node, which needs to
 55		execute under this namespace.
 56		So it is not necessary to declare this transient, because we can
 57		only be saved as part of our namespace anyway... (currently).
 58	*/
 59	NameSpace declaringNameSpace;
 60
 61	private Class [] argTypes;
 62
 63	BshMethod( 
 64		BSHMethodDeclaration method, NameSpace declaringNameSpace ) 
 65	{
 66		this.method = method;
 67		this.declaringNameSpace = declaringNameSpace;
 68		
 69	}
 70
 71	/**
 72		Note: bshmethod needs to re-evaluate arg types here
 73		This is broken
 74	*/
 75	public Class [] getArgTypes() {
 76		if ( argTypes == null )
 77			// should re-eval here...
 78			argTypes = method.params.argTypes ;
 79
 80		return argTypes;
 81	}
 82
 83	/**
 84		Invoke the bsh method with the specified args, interpreter ref,
 85		and callstack.
 86		callerInfo is the node representing the method invocation
 87		It is used primarily for debugging in order to provide access to the 
 88		text of the construct that invoked the method through the namespace.
 89		@param callerInfo is the node representing the method invocation
 90			This is used primarily for debugging and may be null.
 91		@param callstack is the callstack of course.  If you are using a 
 92		hacked version of BeanShell that exposed this method take a look
 93		at NameSpace invokeMethod to see how to make a fake callstack...
 94	*/
 95	public Object invokeDeclaredMethod( 
 96		Object[] argValues, Interpreter interpreter, CallStack callstack,
 97			SimpleNode callerInfo ) 
 98		throws EvalError 
 99	{
100		if ( argValues == null )
101			argValues = new Object [] { };
102
103		// Cardinality (number of args) mismatch
104		if ( argValues.length != method.params.numArgs ) {
105			// look for help string
106			try {
107				// should check for null namespace here
108				String help = 
109					(String)declaringNameSpace.get(
110					"bsh.help."+method.name, interpreter );
111
112				interpreter.println(help);
113				return Primitive.VOID;
114			} catch ( Exception e ) {
115				throw new EvalError( 
116					"Wrong number of arguments for local method: " 
117					+ method.name, callerInfo);
118			}
119		}
120
121		// Make the local namespace for the method invocation
122		NameSpace localNameSpace = new NameSpace( 
123			declaringNameSpace, method.name );
124		localNameSpace.setNode( callerInfo );
125
126		// set the method parameters in the local namespace
127		for(int i=0; i<method.params.numArgs; i++)
128		{
129			// Set typed variable
130			if ( method.params.argTypes[i] != null ) 
131			{
132				try {
133					argValues[i] = NameSpace.getAssignableForm(argValues[i],
134					    method.params.argTypes[i]);
135				}
136				catch(EvalError e) {
137					throw new EvalError(
138						"Invalid argument: " 
139						+ "`"+method.params.argNames[i]+"'" + " for method: " 
140						+ method.name + " : " + 
141						e.getMessage(), callerInfo);
142				}
143				localNameSpace.setTypedVariable( method.params.argNames[i], 
144					method.params.argTypes[i], argValues[i], false);
145			} 
146			// Set untyped variable
147			else  // untyped param
148			{
149				// checkAssignable would catch this for typed param
150				if ( argValues[i] == Primitive.VOID)
151					throw new EvalError(
152						"Undefined variable or class name, parameter: " +
153						method.params.argNames[i] + " to method: " 
154						+ method.name, callerInfo);
155				else
156					localNameSpace.setVariable(
157						method.params.argNames[i], argValues[i]);
158			}
159		}
160
161		// Push the new namespace on the call stack
162		callstack.push( localNameSpace );
163		// Invoke the method
164		Object ret = method.block.eval( callstack, interpreter, true );
165		// pop back to caller namespace
166		callstack.pop();
167
168		if ( ret instanceof ReturnControl )
169		{
170			ReturnControl rs = (ReturnControl)ret;
171			if(rs.kind == rs.RETURN)
172				ret = ((ReturnControl)ret).value;
173			else 
174				// This error points to the method, should it?
175				throw new EvalError("continue or break in method body", method);
176		}
177
178		// there should be a check in here for an explicit value return 
179		// from a void type method... (throw evalerror)
180
181		if(method.returnType != null)
182		{
183			// if void return type throw away any value
184			// ideally, we'd error on an explicit 'return' of value
185			if(method.returnType == Primitive.VOID)
186				return method.returnType;
187
188			// return type is a class
189			try {
190				ret = NameSpace.getAssignableForm(
191					ret, (Class)method.returnType);
192			}
193			catch(EvalError e) {
194				// This error points to the method, should it?
195				throw new EvalError(
196					"Incorrect type returned from method: " 
197					+ method.name + e.getMessage(), method);
198			}
199		}
200
201		return ret;
202	}
203
204	public String toString() {
205		return "Bsh Method: "+method.name;
206	}
207}