All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bsh.BshMethod Maven / Gradle / Ivy

The newest version!
/*
 * #%L
 * The AIBench Shell Plugin
 * %%
 * Copyright (C) 2006 - 2017 Daniel Glez-Peña and Florentino Fdez-Riverola
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */
/*****************************************************************************
 *                                                                           *
 *  This file is part of the BeanShell Java Scripting distribution.          *
 *  Documentation and updates may be found at http://www.beanshell.org/      *
 *                                                                           *
 *  Sun Public License Notice:                                               *
 *                                                                           *
 *  The contents of this file are subject to the Sun Public License Version  *
 *  1.0 (the "License"); you may not use this file except in compliance with *
 *  the License. A copy of the License is available at http://www.sun.com    * 
 *                                                                           *
 *  The Original Code is BeanShell. The Initial Developer of the Original    *
 *  Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright     *
 *  (C) 2000.  All Rights Reserved.                                          *
 *                                                                           *
 *  GNU Public License Notice:                                               *
 *                                                                           *
 *  Alternatively, the contents of this file may be used under the terms of  *
 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
 *  provisions of LGPL are applicable instead of those above. If you wish to *
 *  allow use of your version of this file only under the  terms of the LGPL *
 *  and not to allow others to use your version of this file under the SPL,  *
 *  indicate your decision by deleting the provisions above and replace      *
 *  them with the notice and other provisions required by the LGPL.  If you  *
 *  do not delete the provisions above, a recipient may use your version of  *
 *  this file under either the SPL or the LGPL.                              *
 *                                                                           *
 *  Patrick Niemeyer ([email protected])                                           *
 *  Author of Learning Java, O'Reilly & Associates                           *
 *  http://www.pat.net/~pat/                                                 *
 *                                                                           *
 *****************************************************************************/

package bsh;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 

* This represents an instance of a bsh method declaration in a particular * namespace. This is a thin wrapper around the BSHMethodDeclaration with a * pointer to the declaring namespace. *

* *

* When a method is located in a subordinate namespace or invoked from an * arbitrary namespace it must nontheless execute with its 'super' as the * context in which it was declared. *

*/ /* * Note: this method incorrectly caches the method structure. It needs to be * cleared when the classloader changes. */ public class BshMethod implements java.io.Serializable { /* * This is the namespace in which the method is set. It is a back-reference * for the node, which needs to execute under this namespace. It is not * necessary to declare this transient, because we can only be saved as part * of our namespace anyway... (currently). */ NameSpace declaringNameSpace; // Begin Method components Modifiers modifiers; private String name; private Class creturnType; // Arguments private String[] paramNames; private int numArgs; private Class[] cparamTypes; // Scripted method body BSHBlock methodBody; // Java Method, for a BshObject that delegates to a real Java method private Method javaMethod; private Object javaObject; // End method components BshMethod(BSHMethodDeclaration method, NameSpace declaringNameSpace, Modifiers modifiers) { this(method.name, method.returnType, method.paramsNode.getParamNames(), method.paramsNode.paramTypes, method.blockNode, declaringNameSpace, modifiers); } BshMethod(String name, Class returnType, String[] paramNames, Class[] paramTypes, BSHBlock methodBody, NameSpace declaringNameSpace, Modifiers modifiers) { this.name = name; this.creturnType = returnType; this.paramNames = paramNames; if (paramNames != null) this.numArgs = paramNames.length; this.cparamTypes = paramTypes; this.methodBody = methodBody; this.declaringNameSpace = declaringNameSpace; this.modifiers = modifiers; } /* * Create a BshMethod that delegates to a real Java method upon invocation. * This is used to represent imported object methods. */ BshMethod(Method method, Object object) { this( method.getName(), method.getReturnType(), null/* paramNames */, method.getParameterTypes(), null/* method.block */, null/* declaringNameSpace */, null/* modifiers */ ); this.javaMethod = method; this.javaObject = object; } /** * Returns the argument types of this method. loosely typed (untyped) arguments * will be represented by null argument types. * * @return the argument types of this method. loosely typed (untyped) arguments * will be represented by null argument types. */ /* * Note: bshmethod needs to re-evaluate arg types here This is broken. */ public Class[] getParameterTypes() { return cparamTypes; } public String[] getParameterNames() { return paramNames; } /** * Get the return type of the method. * * @return Returns null for a loosely typed return value, Void.TYPE for a * void return type, or the Class of the type. */ /* * Note: bshmethod needs to re-evaluate the method return type here. This is * broken. */ public Class getReturnType() { return creturnType; } public Modifiers getModifiers() { return modifiers; } public String getName() { return name; } /** * Invoke the declared method with the specified arguments and interpreter * reference. This is the simplest form of invoke() for BshMethod intended * to be used in reflective style access to bsh scripts. * * @param argValues the value of the arguments. * @param interpreter the interpreter. * @return the result of the invocation. * @throws EvalError if an error occurs during invocation. */ public Object invoke(Object[] argValues, Interpreter interpreter) throws EvalError { return invoke(argValues, interpreter, null, null, false); } /** * Invoke the bsh method with the specified args, interpreter ref, and * callstack. callerInfo is the node representing the method invocation It * is used primarily for debugging in order to provide access to the text of * the construct that invoked the method through the namespace. * * @param argValues the value of the arguments. * @param interpreter the interpreter. * @param callerInfo * is the BeanShell AST node representing the method invocation. * It is used to print the line number and text of errors in * EvalError exceptions. If the node is null here error messages * may not be able to point to the precise location and text of * the error. * @param callstack * is the callstack. If callstack is null a new one will be * created with the declaring namespace of the method on top of * the stack (i.e. it will look for purposes of the method * invocation like the method call occurred in the declaring * (enclosing) namespace in which the method is defined). * @return the result of the invocation. * @throws EvalError if an error occurs during invocation. */ public Object invoke(Object[] argValues, Interpreter interpreter, CallStack callstack, SimpleNode callerInfo) throws EvalError { return invoke(argValues, interpreter, callstack, callerInfo, false); } /** * Invoke the bsh method with the specified args, interpreter ref, and * callstack. callerInfo is the node representing the method invocation It * is used primarily for debugging in order to provide access to the text of * the construct that invoked the method through the namespace. * * @param argValues the value of the arguments. * @param interpreter the interpreter. * @param callerInfo * is the BeanShell AST node representing the method invocation. * It is used to print the line number and text of errors in * EvalError exceptions. If the node is null here error messages * may not be able to point to the precise location and text of * the error. * @param callstack * is the callstack. If callstack is null a new one will be * created with the declaring namespace of the method on top of * the stack (i.e. it will look for purposes of the method * invocation like the method call occurred in the declaring * (enclosing) namespace in which the method is defined). * @param overrideNameSpace * When true the method is executed in the namespace on the top * of the stack instead of creating its own local namespace. This * allows it to be used in constructors. * @return the result of the invocation. * @throws EvalError if an error occurs during invocation. */ Object invoke(Object[] argValues, Interpreter interpreter, CallStack callstack, SimpleNode callerInfo, boolean overrideNameSpace) throws EvalError { if (argValues != null) for (int i = 0; i < argValues.length; i++) if (argValues[i] == null) throw new Error("HERE!"); if (javaMethod != null) try { return Reflect.invokeMethod(javaMethod, javaObject, argValues); } catch (ReflectError e) { throw new EvalError("Error invoking Java method: " + e, callerInfo, callstack); } catch (InvocationTargetException e2) { throw new TargetError("Exception invoking imported object method.", e2, callerInfo, callstack, true/* isNative */); } // is this a syncrhonized method? if (modifiers != null && modifiers.hasModifier("synchronized")) { // The lock is our declaring namespace's This reference // (the method's 'super'). Or in the case of a class // it's the // class instance. Object lock; if (declaringNameSpace.isClass) { try { lock = declaringNameSpace.getClassInstance(); } catch (UtilEvalError e) { throw new InterpreterError("Can't get class instance for synchronized method."); } } else lock = declaringNameSpace.getThis(interpreter); // ??? synchronized (lock) { return invokeImpl(argValues, interpreter, callstack, callerInfo, overrideNameSpace); } } else return invokeImpl(argValues, interpreter, callstack, callerInfo, overrideNameSpace); } private Object invokeImpl(Object[] argValues, Interpreter interpreter, CallStack callstack, SimpleNode callerInfo, boolean overrideNameSpace) throws EvalError { Class returnType = getReturnType(); Class[] paramTypes = getParameterTypes(); // If null callstack if (callstack == null) callstack = new CallStack(declaringNameSpace); if (argValues == null) argValues = new Object[] {}; // Cardinality (number of args) mismatch if (argValues.length != numArgs) { /* * // look for help string try { // should check for null namespace * here String help = (String)declaringNameSpace.get( * "bsh.help."+name, interpreter ); * * interpreter.println(help); return Primitive.VOID; } catch ( * Exception e ) { throw eval error } */ throw new EvalError("Wrong number of arguments for local method: " + name, callerInfo, callstack); } // Make the local namespace for the method invocation NameSpace localNameSpace; if (overrideNameSpace) localNameSpace = callstack.top(); else { localNameSpace = new NameSpace(declaringNameSpace, name); localNameSpace.isMethod = true; } // should we do this for both cases above? localNameSpace.setNode(callerInfo); // set the method parameters in the local namespace for (int i = 0; i < numArgs; i++) { // Set typed variable if (paramTypes[i] != null) { try { argValues[i] = // Types.getAssignableForm( // argValues[i], paramTypes[i] ); Types.castObject(argValues[i], paramTypes[i], Types.ASSIGNMENT); } catch (UtilEvalError e) { throw new EvalError( "Invalid argument: " + "`" + paramNames[i] + "'" + " for method: " + name + " : " + e.getMessage(), callerInfo, callstack ); } try { localNameSpace.setTypedVariable(paramNames[i], paramTypes[i], argValues[i], null/* modifiers */); } catch (UtilEvalError e2) { throw e2.toEvalError("Typed method parameter assignment", callerInfo, callstack); } } // Set untyped variable else // untyped param { // getAssignable would catch this for typed // param if (argValues[i] == Primitive.VOID) throw new EvalError("Undefined variable or class name, parameter: " + paramNames[i] + " to method: " + name, callerInfo, callstack); else try { localNameSpace.setLocalVariable(paramNames[i], argValues[i], interpreter.getStrictJava()); } catch (UtilEvalError e3) { throw e3.toEvalError(callerInfo, callstack); } } } // Push the new namespace on the call stack if (!overrideNameSpace) callstack.push(localNameSpace); // Invoke the block, overriding namespace with localNameSpace Object ret = methodBody.eval(callstack, interpreter, true/* override */); // save the callstack including the called method, just for // error mess CallStack returnStack = callstack.copy(); // Get back to caller namespace if (!overrideNameSpace) callstack.pop(); ReturnControl retControl = null; if (ret instanceof ReturnControl) { retControl = (ReturnControl) ret; // Method body can only use 'return' statment type // return control. if (retControl.kind == retControl.RETURN) ret = ((ReturnControl) ret).value; else // retControl.returnPoint is the Node of the // return statement throw new EvalError("'continue' or 'break' in method body", retControl.returnPoint, returnStack); // Check for explicit return of value from void method // type. // retControl.returnPoint is the Node of the return // statement if (returnType == Void.TYPE && ret != Primitive.VOID) throw new EvalError("Cannot return value from void method", retControl.returnPoint, returnStack); } if (returnType != null) { // If return type void, return void as the value. if (returnType == Void.TYPE) return Primitive.VOID; // return type is a class try { ret = // Types.getAssignableForm( ret, // (Class)returnType ); Types.castObject(ret, returnType, Types.ASSIGNMENT); } catch (UtilEvalError e) { // Point to return statement point if we had // one. // (else it was implicit return? What's the case // here?) SimpleNode node = callerInfo; if (retControl != null) node = retControl.returnPoint; throw e.toEvalError("Incorrect type returned from method: " + name + e.getMessage(), node, callstack); } } return ret; } public boolean hasModifier(String name) { return modifiers != null && modifiers.hasModifier(name); } public String toString() { return "Scripted Method: " + StringUtil.methodString(name, getParameterTypes()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy