96
// in org/gjt/sp/jedit/bsh/BSHUnaryExpression.java
public Object eval( CallStack callstack, Interpreter interpreter)
throws EvalError
{
SimpleNode node = (SimpleNode)jjtGetChild(0);
// If this is a unary increment of decrement (either pre or postfix)
// then we need an LHS to which to assign the result. Otherwise
// just do the unary operation for the value.
try {
if ( kind == INCR || kind == DECR ) {
LHS lhs = ((BSHPrimaryExpression)node).toLHS(
callstack, interpreter );
return lhsUnaryOperation( lhs, interpreter.getStrictJava() );
} else
return
unaryOperation( node.eval(callstack, interpreter), kind );
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BSHPrimarySuffix.java
public Object doSuffix(
Object obj, boolean toLHS,
CallStack callstack, Interpreter interpreter)
throws EvalError
{
// Handle ".class" suffix operation
// Prefix must be a BSHType
if ( operation == CLASS )
if ( obj instanceof BSHType ) {
if ( toLHS )
throw new EvalError("Can't assign .class",
this, callstack );
NameSpace namespace = callstack.top();
return ((BSHType)obj).getType( callstack, interpreter );
} else
throw new EvalError(
"Attempt to use .class suffix on non class.",
this, callstack );
/*
Evaluate our prefix if it needs evaluating first.
If this is the first evaluation our prefix mayb be a Node
(directly from the PrimaryPrefix) - eval() it to an object.
If it's an LHS, resolve to a value.
Note: The ambiguous name construct is now necessary where the node
may be an ambiguous name. If this becomes common we might want to
make a static method nodeToObject() or something. The point is
that we can't just eval() - we need to direct the evaluation to
the context sensitive type of result; namely object, class, etc.
*/
if ( obj instanceof SimpleNode )
if ( obj instanceof BSHAmbiguousName )
obj = ((BSHAmbiguousName)obj).toObject(callstack, interpreter);
else
obj = ((SimpleNode)obj).eval(callstack, interpreter);
else
if ( obj instanceof LHS )
try {
obj = ((LHS)obj).getValue();
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
try
{
switch(operation)
{
case INDEX:
return doIndex( obj, toLHS, callstack, interpreter );
case NAME:
return doName( obj, toLHS, callstack, interpreter );
case PROPERTY:
return doProperty( toLHS, obj, callstack, interpreter );
default:
throw new InterpreterError( "Unknown suffix type" );
}
}
catch(ReflectError e)
{
throw new EvalError("reflection error: " + e, this, callstack );
}
catch(InvocationTargetException e)
{
throw new TargetError( "target exception", e.getTargetException(),
this, callstack, true);
}
}
// in org/gjt/sp/jedit/bsh/BSHPrimarySuffix.java
private Object doName(
Object obj, boolean toLHS,
CallStack callstack, Interpreter interpreter)
throws EvalError, ReflectError, InvocationTargetException
{
try {
// .length on array
if ( field.equals("length") && obj.getClass().isArray() )
if ( toLHS )
throw new EvalError(
"Can't assign array length", this, callstack );
else
return new Primitive(Array.getLength(obj));
// field access
if ( jjtGetNumChildren() == 0 )
if ( toLHS )
return Reflect.getLHSObjectField(obj, field);
else
return Reflect.getObjectFieldValue( obj, field );
// Method invocation
// (LHS or non LHS evaluation can both encounter method calls)
Object[] oa = ((BSHArguments)jjtGetChild(0)).getArguments(
callstack, interpreter);
// TODO:
// Note: this try/catch block is copied from BSHMethodInvocation
// we need to factor out this common functionality and make sure
// we handle all cases ... (e.g. property style access, etc.)
// maybe move this to Reflect ?
try {
return Reflect.invokeObjectMethod(
obj, field, oa, interpreter, callstack, this );
} catch ( ReflectError e ) {
throw new EvalError(
"Error in method invocation: " + e.getMessage(),
this, callstack );
} catch ( InvocationTargetException e )
{
String msg = "Method Invocation "+field;
Throwable te = e.getTargetException();
/*
Try to squeltch the native code stack trace if the exception
was caused by a reflective call back into the bsh interpreter
(e.g. eval() or source()
*/
boolean isNative = true;
if ( te instanceof EvalError )
if ( te instanceof TargetError )
isNative = ((TargetError)te).inNativeCode();
else
isNative = false;
throw new TargetError( msg, te, this, callstack, isNative );
}
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BSHPrimarySuffix.java
static int getIndexAux(
Object obj, CallStack callstack, Interpreter interpreter,
SimpleNode callerInfo )
throws EvalError
{
if ( !obj.getClass().isArray() )
throw new EvalError("Not an array", callerInfo, callstack );
int index;
try {
Object indexVal =
((SimpleNode)callerInfo.jjtGetChild(0)).eval(
callstack, interpreter );
if ( !(indexVal instanceof Primitive) )
indexVal = Types.castObject(
indexVal, Integer.TYPE, Types.ASSIGNMENT );
index = ((Primitive)indexVal).intValue();
} catch( UtilEvalError e ) {
Interpreter.debug("doIndex: "+e);
throw e.toEvalError(
"Arrays may only be indexed by integer types.",
callerInfo, callstack );
}
return index;
}
// in org/gjt/sp/jedit/bsh/BSHPrimarySuffix.java
private Object doIndex(
Object obj, boolean toLHS,
CallStack callstack, Interpreter interpreter )
throws EvalError, ReflectError
{
int index = getIndexAux( obj, callstack, interpreter, this );
if ( toLHS )
return new LHS(obj, index);
else
try {
return Reflect.getIndex(obj, index);
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BSHPrimarySuffix.java
private Object doProperty( boolean toLHS,
Object obj, CallStack callstack, Interpreter interpreter )
throws EvalError
{
if(obj == Primitive.VOID)
throw new EvalError(
"Attempt to access property on undefined variable or class name",
this, callstack );
if ( obj instanceof Primitive )
throw new EvalError("Attempt to access property on a primitive",
this, callstack );
Object value = ((SimpleNode)jjtGetChild(0)).eval(
callstack, interpreter);
if ( !( value instanceof String ) )
throw new EvalError(
"Property expression must be a String or identifier.",
this, callstack );
if ( toLHS )
return new LHS(obj, (String)value);
// Property style access to Hashtable or Map
CollectionManager cm = CollectionManager.getCollectionManager();
if ( cm.isMap( obj ) )
{
Object val = cm.getFromMap( obj, value/*key*/ );
return ( val == null ? val = Primitive.NULL : val );
}
try {
return Reflect.getObjectProperty( obj, (String)value );
}
catch ( UtilEvalError e)
{
throw e.toEvalError( "Property: "+value, this, callstack );
}
catch (ReflectError e)
{
throw new EvalError("No such property: " + value, this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BshMethod.java
public Object invoke(
Object[] argValues, Interpreter interpreter )
throws EvalError
{
return invoke( argValues, interpreter, null, null, false );
}
// in org/gjt/sp/jedit/bsh/BshMethod.java
public Object invoke(
Object[] argValues, Interpreter interpreter, CallStack callstack,
SimpleNode callerInfo )
throws EvalError
{
return invoke( argValues, interpreter, callstack, callerInfo, false );
}
// in org/gjt/sp/jedit/bsh/BshMethod.java
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 );
}
// in org/gjt/sp/jedit/bsh/BshMethod.java
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;
}
// in org/gjt/sp/jedit/bsh/This.java
public Object invokeMethod( String name, Object [] args )
throws EvalError
{
// null callstack, one will be created for us
return invokeMethod(
name, args, null/*declaringInterpreter*/, null, null,
false/*declaredOnly*/ );
}
// in org/gjt/sp/jedit/bsh/This.java
public Object invokeMethod(
String methodName, Object [] args,
Interpreter interpreter, CallStack callstack, SimpleNode callerInfo,
boolean declaredOnly )
throws EvalError
{
/*
Wrap nulls.
This is a bit of a cludge to address a deficiency in the class
generator whereby it does not wrap nulls on method delegate. See
Class Generator.java. If we fix that then we can remove this.
(just have to generate the code there.)
*/
if ( args != null )
{
Object [] oa = new Object [args.length];
for(int i=0; i<args.length; i++)
oa[i] = ( args[i] == null ? Primitive.NULL : args[i] );
args = oa;
}
if ( interpreter == null )
interpreter = declaringInterpreter;
if ( callstack == null )
callstack = new CallStack( namespace );
if ( callerInfo == null )
callerInfo = SimpleNode.JAVACODE;
// Find the bsh method
Class [] types = Types.getTypes( args );
BshMethod bshMethod = null;
try {
bshMethod = namespace.getMethod( methodName, types, declaredOnly );
} catch ( UtilEvalError e ) {
// leave null
}
if ( bshMethod != null )
return bshMethod.invoke( args, interpreter, callstack, callerInfo );
/*
No scripted method of that name.
Implement the required part of the Object protocol:
public int hashCode();
public boolean equals(java.lang.Object);
public java.lang.String toString();
if these were not handled by scripted methods we must provide
a default impl.
*/
// a default toString() that shows the interfaces we implement
if ( methodName.equals("toString" ) )
return toString();
// a default hashCode()
if ( methodName.equals("hashCode" ) )
return new Integer(this.hashCode());
// a default equals() testing for equality with the This reference
if ( methodName.equals("equals" ) ) {
Object obj = args[0];
return new Boolean( this == obj );
}
// Look for a default invoke() handler method in the namespace
// Note: this code duplicates that in NameSpace getCommand()
// is that ok?
try {
bshMethod = namespace.getMethod(
"invoke", new Class [] { null, null } );
} catch ( UtilEvalError e ) { /*leave null*/ }
// Call script "invoke( String methodName, Object [] args );
if ( bshMethod != null )
return bshMethod.invoke( new Object [] { methodName, args },
interpreter, callstack, callerInfo );
throw new EvalError("Method " +
StringUtil.methodString( methodName, types ) +
" not found in bsh scripted object: "+ namespace.getName(),
callerInfo, callstack );
}
// in org/gjt/sp/jedit/bsh/BSHArguments.java
public Object[] getArguments( CallStack callstack, Interpreter interpreter)
throws EvalError
{
// evaluate each child
Object[] args = new Object[jjtGetNumChildren()];
for(int i = 0; i < args.length; i++)
{
args[i] = ((SimpleNode)jjtGetChild(i)).eval(callstack, interpreter);
if ( args[i] == Primitive.VOID )
throw new EvalError( "Undefined argument: " +
((SimpleNode)jjtGetChild(i)).getText(), this, callstack );
}
return args;
}
// in org/gjt/sp/jedit/bsh/ClassGeneratorImpl.java
public Class generateClass(
String name, Modifiers modifiers,
Class [] interfaces, Class superClass, BSHBlock block,
boolean isInterface, CallStack callstack, Interpreter interpreter
)
throws EvalError
{
// Delegate to the static method
return generateClassImpl( name, modifiers, interfaces, superClass,
block, isInterface, callstack, interpreter );
}
// in org/gjt/sp/jedit/bsh/ClassGeneratorImpl.java
public static Class generateClassImpl(
String name, Modifiers modifiers,
Class [] interfaces, Class superClass, BSHBlock block,
boolean isInterface, CallStack callstack, Interpreter interpreter
)
throws EvalError
{
// Scripting classes currently requires accessibility
// This can be eliminated with a bit more work.
try {
Capabilities.setAccessibility( true );
} catch ( Capabilities.Unavailable e )
{
throw new EvalError(
"Defining classes currently requires reflective Accessibility.",
block, callstack );
}
NameSpace enclosingNameSpace = callstack.top();
String packageName = enclosingNameSpace.getPackage();
String className = enclosingNameSpace.isClass ?
( enclosingNameSpace.getName()+"$"+name ) : name;
String fqClassName =
packageName == null ? className : packageName + "." + className;
BshClassManager bcm = interpreter.getClassManager();
// Race condition here...
bcm.definingClass( fqClassName );
// Create the class static namespace
NameSpace classStaticNameSpace =
new NameSpace( enclosingNameSpace, className);
classStaticNameSpace.isClass = true;
callstack.push( classStaticNameSpace );
// Evaluate any inner class class definitions in the block
// effectively recursively call this method for contained classes first
block.evalBlock(
callstack, interpreter, true/*override*/,
ClassNodeFilter.CLASSCLASSES );
// Generate the type for our class
Variable [] variables =
getDeclaredVariables( block, callstack, interpreter, packageName );
DelayedEvalBshMethod [] methods =
getDeclaredMethods( block, callstack, interpreter, packageName );
ClassGeneratorUtil classGenerator = new ClassGeneratorUtil(
modifiers, className, packageName, superClass, interfaces,
variables, methods, classStaticNameSpace, isInterface );
byte [] code = classGenerator.generateClass();
// if debug, write out the class file to debugClasses directory
String dir = System.getProperty("debugClasses");
if ( dir != null )
try {
FileOutputStream out=
new FileOutputStream( dir+"/"+className+".class" );
out.write(code);
out.close();
} catch ( IOException e ) { }
// Define the new class in the classloader
Class genClass = bcm.defineClass( fqClassName, code );
// import the unq name into parent
enclosingNameSpace.importClass( fqClassName.replace('$','.') );
try {
classStaticNameSpace.setLocalVariable(
ClassGeneratorUtil.BSHINIT, block, false/*strictJava*/ );
} catch ( UtilEvalError e ) {
throw new InterpreterError("unable to init static: "+e );
}
// Give the static space its class static import
// important to do this after all classes are defined
classStaticNameSpace.setClassStatic( genClass );
// evaluate the static portion of the block in the static space
block.evalBlock(
callstack, interpreter, true/*override*/,
ClassNodeFilter.CLASSSTATIC );
callstack.pop();
if ( !genClass.isInterface() )
{
// Set the static bsh This callback
String bshStaticFieldName = ClassGeneratorUtil.BSHSTATIC+className;
try {
LHS lhs = Reflect.getLHSStaticField( genClass, bshStaticFieldName );
lhs.assign(
classStaticNameSpace.getThis( interpreter ), false/*strict*/ );
} catch ( Exception e ) {
throw new InterpreterError("Error in class gen setup: "+e );
}
}
bcm.doneDefiningClass( fqClassName );
return genClass;
}
// in org/gjt/sp/jedit/bsh/ClassGeneratorImpl.java
static DelayedEvalBshMethod [] getDeclaredMethods(
BSHBlock body, CallStack callstack, Interpreter interpreter,
String defaultPackage
)
throws EvalError
{
List methods = new ArrayList();
for( int child=0; child<body.jjtGetNumChildren(); child++ )
{
SimpleNode node = (SimpleNode)body.jjtGetChild(child);
if ( node instanceof BSHMethodDeclaration )
{
BSHMethodDeclaration md = (BSHMethodDeclaration)node;
md.insureNodesParsed();
Modifiers modifiers = md.modifiers;
String name = md.name;
String returnType = md.getReturnTypeDescriptor(
callstack, interpreter, defaultPackage );
BSHReturnType returnTypeNode = md.getReturnTypeNode();
BSHFormalParameters paramTypesNode = md.paramsNode;
String [] paramTypes = paramTypesNode.getTypeDescriptors(
callstack, interpreter, defaultPackage );
DelayedEvalBshMethod bm = new DelayedEvalBshMethod(
name,
returnType, returnTypeNode,
md.paramsNode.getParamNames(),
paramTypes, paramTypesNode,
md.blockNode, null/*declaringNameSpace*/,
modifiers, callstack, interpreter
);
methods.add( bm );
}
}
return (DelayedEvalBshMethod [])methods.toArray(
new DelayedEvalBshMethod[0] );
}
// in org/gjt/sp/jedit/bsh/Reflect.java
public static Object invokeObjectMethod(
Object object, String methodName, Object[] args,
Interpreter interpreter, CallStack callstack, SimpleNode callerInfo )
throws ReflectError, EvalError, InvocationTargetException
{
// Bsh scripted object
if ( object instanceof This && !This.isExposedThisMethod(methodName) )
return ((This)object).invokeMethod(
methodName, args, interpreter, callstack, callerInfo,
false/*delcaredOnly*/
);
// Plain Java object, find the java method
try {
BshClassManager bcm =
interpreter == null ? null : interpreter.getClassManager();
Class clas = object.getClass();
Method method = resolveExpectedJavaMethod(
bcm, clas, object, methodName, args, false );
return invokeMethod( method, object, args );
} catch ( UtilEvalError e ) {
throw e.toEvalError( callerInfo, callstack );
}
}
// in org/gjt/sp/jedit/bsh/SimpleNode.java
public Object eval( CallStack callstack, Interpreter interpreter )
throws EvalError
{
throw new InterpreterError(
"Unimplemented or inappropriate for " + getClass().getName() );
}
// in org/gjt/sp/jedit/bsh/BSHAmbiguousName.java
public Object toObject( CallStack callstack, Interpreter interpreter )
throws EvalError
{
return toObject( callstack, interpreter, false );
}
// in org/gjt/sp/jedit/bsh/BSHAmbiguousName.java
Object toObject(
CallStack callstack, Interpreter interpreter, boolean forceClass )
throws EvalError
{
try {
return
getName( callstack.top() ).toObject(
callstack, interpreter, forceClass );
} catch ( UtilEvalError e ) {
//e.printStackTrace();
throw e.toEvalError( this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BSHAmbiguousName.java
public Class toClass( CallStack callstack, Interpreter interpreter )
throws EvalError
{
try {
return getName( callstack.top() ).toClass();
} catch ( ClassNotFoundException e ) {
throw new EvalError( e.getMessage(), this, callstack );
} catch ( UtilEvalError e2 ) {
// ClassPathException is a type of UtilEvalError
throw e2.toEvalError( this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BSHAmbiguousName.java
public LHS toLHS( CallStack callstack, Interpreter interpreter)
throws EvalError
{
try {
return getName( callstack.top() ).toLHS( callstack, interpreter );
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BSHAmbiguousName.java
public Object eval( CallStack callstack, Interpreter interpreter )
throws EvalError
{
throw new InterpreterError(
"Don't know how to eval an ambiguous name!"
+" Use toObject() if you want an object." );
}
// in org/gjt/sp/jedit/bsh/XThis.java
public Object invokeImpl( Object proxy, Method method, Object[] args )
throws EvalError
{
String methodName = method.getName();
CallStack callstack = new CallStack( namespace );
/*
If equals() is not explicitly defined we must override the
default implemented by the This object protocol for scripted
object. To support XThis equals() must test for equality with
the generated proxy object, not the scripted bsh This object;
otherwise callers from outside in Java will not see a the
proxy object as equal to itself.
*/
BshMethod equalsMethod = null;
try {
equalsMethod = namespace.getMethod(
"equals", new Class [] { Object.class } );
} catch ( UtilEvalError e ) {/*leave null*/ }
if ( methodName.equals("equals" ) && equalsMethod == null ) {
Object obj = args[0];
return new Boolean( proxy == obj );
}
/*
If toString() is not explicitly defined override the default
to show the proxy interfaces.
*/
BshMethod toStringMethod = null;
try {
toStringMethod =
namespace.getMethod( "toString", new Class [] { } );
} catch ( UtilEvalError e ) {/*leave null*/ }
if ( methodName.equals("toString" ) && toStringMethod == null)
{
Class [] ints = proxy.getClass().getInterfaces();
// XThis.this refers to the enclosing class instance
StringBuilder sb = new StringBuilder(
XThis.this.toString() + "\nimplements:" );
for(int i=0; i<ints.length; i++)
sb.append( " "+ ints[i].getName()
+ ((ints.length > 1)?",":"") );
return sb.toString();
}
Class [] paramTypes = method.getParameterTypes();
return Primitive.unwrap(
invokeMethod( methodName, Primitive.wrap(args, paramTypes) ) );
}
// in org/gjt/sp/jedit/bsh/BSHCastExpression.java
public Object eval(
CallStack callstack, Interpreter interpreter ) throws EvalError
{
NameSpace namespace = callstack.top();
Class toType = ((BSHType)jjtGetChild(0)).getType(
callstack, interpreter );
SimpleNode expression = (SimpleNode)jjtGetChild(1);
// evaluate the expression
Object fromValue = expression.eval(callstack, interpreter);
Class fromType = fromValue.getClass();
// TODO: need to add isJavaCastable() test for strictJava
// (as opposed to isJavaAssignable())
try {
return Types.castObject( fromValue, toType, Types.CAST );
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BSHPrimaryExpression.java
public Object eval( CallStack callstack, Interpreter interpreter)
throws EvalError
{
return eval( false, callstack, interpreter );
}
// in org/gjt/sp/jedit/bsh/BSHPrimaryExpression.java
public LHS toLHS( CallStack callstack, Interpreter interpreter)
throws EvalError
{
Object obj = eval( true, callstack, interpreter );
if ( ! (obj instanceof LHS) )
throw new EvalError("Can't assign to:", this, callstack );
else
return (LHS)obj;
}
// in org/gjt/sp/jedit/bsh/BSHPrimaryExpression.java
private Object eval( boolean toLHS,
CallStack callstack, Interpreter interpreter)
throws EvalError
{
Object obj = jjtGetChild(0);
int numChildren = jjtGetNumChildren();
for(int i=1; i<numChildren; i++)
obj = ((BSHPrimarySuffix)jjtGetChild(i)).doSuffix(
obj, toLHS, callstack, interpreter);
/*
If the result is a Node eval() it to an object or LHS
(as determined by toLHS)
*/
if ( obj instanceof SimpleNode )
if ( obj instanceof BSHAmbiguousName )
if ( toLHS )
obj = ((BSHAmbiguousName)obj).toLHS(
callstack, interpreter);
else
obj = ((BSHAmbiguousName)obj).toObject(
callstack, interpreter);
else
// Some arbitrary kind of node
if ( toLHS )
// is this right?
throw new EvalError("Can't assign to prefix.",
this, callstack );
else
obj = ((SimpleNode)obj).eval(callstack, interpreter);
// return LHS or value object as determined by toLHS
if ( obj instanceof LHS )
if ( toLHS )
return obj;
else
try {
return ((LHS)obj).getValue();
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
else
return obj;
}
// in org/gjt/sp/jedit/bsh/BSHLiteral.java
public Object eval( CallStack callstack, Interpreter interpreter )
throws EvalError
{
if ( value == null )
throw new InterpreterError("Null in bsh literal: "+value);
return value;
}
// in org/gjt/sp/jedit/bsh/BSHTypedVariableDeclaration.java
Class evalType( CallStack callstack, Interpreter interpreter )
throws EvalError
{
BSHType typeNode = getTypeNode();
return typeNode.getType( callstack, interpreter );
}
// in org/gjt/sp/jedit/bsh/BSHTypedVariableDeclaration.java
public Object eval( CallStack callstack, Interpreter interpreter)
throws EvalError
{
try {
NameSpace namespace = callstack.top();
BSHType typeNode = getTypeNode();
Class type = typeNode.getType( callstack, interpreter );
BSHVariableDeclarator [] bvda = getDeclarators();
for (int i = 0; i < bvda.length; i++)
{
BSHVariableDeclarator dec = bvda[i];
// Type node is passed down the chain for array initializers
// which need it under some circumstances
Object value = dec.eval( typeNode, callstack, interpreter);
try {
namespace.setTypedVariable(
dec.name, type, value, modifiers );
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
}
} catch ( EvalError e ) {
e.reThrow( "Typed variable declaration" );
}
return Primitive.VOID;
}
// in org/gjt/sp/jedit/bsh/BSHWhileStatement.java
public Object eval( CallStack callstack, Interpreter interpreter)
throws EvalError
{
int numChild = jjtGetNumChildren();
// Order of body and condition is swapped for do / while
SimpleNode condExp, body = null;
if ( isDoStatement ) {
condExp = (SimpleNode)jjtGetChild(1);
body =(SimpleNode)jjtGetChild(0);
} else {
condExp = (SimpleNode)jjtGetChild(0);
if ( numChild > 1 ) // has body, else just for side effects
body =(SimpleNode)jjtGetChild(1);
}
boolean doOnceFlag = isDoStatement;
while(
doOnceFlag ||
BSHIfStatement.evaluateCondition(condExp, callstack, interpreter )
)
{
if ( body == null ) // no body?
continue;
Object ret = body.eval(callstack, interpreter);
boolean breakout = false;
if(ret instanceof ReturnControl)
{
switch(((ReturnControl)ret).kind )
{
case RETURN:
return ret;
case CONTINUE:
continue;
case BREAK:
breakout = true;
break;
}
}
if(breakout)
break;
doOnceFlag = false;
}
return Primitive.VOID;
}
// in org/gjt/sp/jedit/bsh/BSHType.java
public Class getType( CallStack callstack, Interpreter interpreter )
throws EvalError
{
// return cached type if available
if ( type != null )
return type;
// first node will either be PrimitiveType or AmbiguousName
SimpleNode node = getTypeNode();
if ( node instanceof BSHPrimitiveType )
baseType = ((BSHPrimitiveType)node).getType();
else
baseType = ((BSHAmbiguousName)node).toClass(
callstack, interpreter );
if ( arrayDims > 0 ) {
try {
// Get the type by constructing a prototype array with
// arbitrary (zero) length in each dimension.
int[] dims = new int[arrayDims]; // int array default zeros
Object obj = Array.newInstance(baseType, dims);
type = obj.getClass();
} catch(Exception e) {
throw new EvalError("Couldn't construct array type",
this, callstack );
}
} else
type = baseType;
// hack... sticking to first interpreter that resolves this
// see comments on type instance variable
interpreter.getClassManager().addListener(this);
return type;
}
// in org/gjt/sp/jedit/bsh/BSHForStatement.java
public Object eval(CallStack callstack , Interpreter interpreter)
throws EvalError
{
int i = 0;
if(hasForInit)
forInit = ((SimpleNode)jjtGetChild(i++));
if(hasExpression)
expression = ((SimpleNode)jjtGetChild(i++));
if(hasForUpdate)
forUpdate = ((SimpleNode)jjtGetChild(i++));
if(i < jjtGetNumChildren()) // should normally be
statement = ((SimpleNode)jjtGetChild(i));
NameSpace enclosingNameSpace= callstack.top();
BlockNameSpace forNameSpace = new BlockNameSpace( enclosingNameSpace );
/*
Note: some interesting things are going on here.
1) We swap instead of push... The primary mode of operation
acts like we are in the enclosing namespace... (super must be
preserved, etc.)
2) We do *not* call the body block eval with the namespace
override. Instead we allow it to create a second subordinate
BlockNameSpace child of the forNameSpace. Variable propogation
still works through the chain, but the block's child cleans the
state between iteration.
(which is correct Java behavior... see forscope4.bsh)
*/
// put forNameSpace it on the top of the stack
// Note: it's important that there is only one exit point from this
// method so that we can swap back the namespace.
callstack.swap( forNameSpace );
// Do the for init
if ( hasForInit )
forInit.eval( callstack, interpreter );
Object returnControl = Primitive.VOID;
while(true)
{
if ( hasExpression )
{
boolean cond = BSHIfStatement.evaluateCondition(
expression, callstack, interpreter );
if ( !cond )
break;
}
boolean breakout = false; // switch eats a multi-level break here?
if ( statement != null ) // not empty statement
{
// do *not* invoke special override for block... (see above)
Object ret = statement.eval( callstack, interpreter );
if (ret instanceof ReturnControl)
{
switch(((ReturnControl)ret).kind)
{
case RETURN:
returnControl = ret;
breakout = true;
break;
case CONTINUE:
break;
case BREAK:
breakout = true;
break;
}
}
}
if ( breakout )
break;
if ( hasForUpdate )
forUpdate.eval( callstack, interpreter );
}
callstack.swap( enclosingNameSpace ); // put it back
return returnControl;
}
// in org/gjt/sp/jedit/bsh/BSHTryStatement.java
public Object eval( CallStack callstack, Interpreter interpreter)
throws EvalError
{
BSHBlock tryBlock = ((BSHBlock)jjtGetChild(0));
Vector catchParams = new Vector();
Vector catchBlocks = new Vector();
int nchild = jjtGetNumChildren();
Node node = null;
int i=1;
while((i < nchild) && ((node = jjtGetChild(i++)) instanceof BSHFormalParameter))
{
catchParams.addElement(node);
catchBlocks.addElement(jjtGetChild(i++));
node = null;
}
// finaly block
BSHBlock finallyBlock = null;
if(node != null)
finallyBlock = (BSHBlock)node;
// Why both of these?
TargetError target = null;
Throwable thrown = null;
Object ret = null;
/*
Evaluate the contents of the try { } block and catch any resulting
TargetErrors generated by the script.
We save the callstack depth and if an exception is thrown we pop
back to that depth before contiuing. The exception short circuited
any intervening method context pops.
Note: we the stack info... what do we do with it? append
to exception message?
*/
int callstackDepth = callstack.depth();
try {
ret = tryBlock.eval(callstack, interpreter);
}
catch( TargetError e ) {
target = e;
String stackInfo = "Bsh Stack: ";
while ( callstack.depth() > callstackDepth )
stackInfo += "\t" + callstack.pop() +"\n";
}
// unwrap the target error
if ( target != null )
thrown = target.getTarget();
// If we have an exception, find a catch
if (thrown != null)
{
int n = catchParams.size();
for(i=0; i<n; i++)
{
// Get catch block
BSHFormalParameter fp =
(BSHFormalParameter)catchParams.elementAt(i);
// Should cache this subject to classloader change message
// Evaluation of the formal parameter simply resolves its
// type via the specified namespace.. it doesn't modify the
// namespace.
fp.eval( callstack, interpreter );
if ( fp.type == null && interpreter.getStrictJava() )
throw new EvalError(
"(Strict Java) Untyped catch block", this, callstack );
// If the param is typed check assignability
if ( fp.type != null )
try {
thrown = (Throwable)Types.castObject(
thrown/*rsh*/, fp.type/*lhsType*/, Types.ASSIGNMENT );
} catch( UtilEvalError e ) {
/*
Catch the mismatch and continue to try the next
Note: this is innefficient, should have an
isAssignableFrom() that doesn't throw
// TODO: we do now have a way to test assignment
// in castObject(), use it?
*/
continue;
}
// Found match, execute catch block
BSHBlock cb = (BSHBlock)(catchBlocks.elementAt(i));
// Prepare to execute the block.
// We must create a new BlockNameSpace to hold the catch
// parameter and swap it on the stack after initializing it.
NameSpace enclosingNameSpace = callstack.top();
BlockNameSpace cbNameSpace =
new BlockNameSpace( enclosingNameSpace );
try {
if ( fp.type == BSHFormalParameter.UNTYPED )
// set an untyped variable directly in the block
cbNameSpace.setBlockVariable( fp.name, thrown );
else
{
// set a typed variable (directly in the block)
Modifiers modifiers = new Modifiers();
cbNameSpace.setTypedVariable(
fp.name, fp.type, thrown, new Modifiers()/*none*/ );
}
} catch ( UtilEvalError e ) {
throw new InterpreterError(
"Unable to set var in catch block namespace." );
}
// put cbNameSpace on the top of the stack
callstack.swap( cbNameSpace );
try {
ret = cb.eval( callstack, interpreter );
} finally {
// put it back
callstack.swap( enclosingNameSpace );
}
target = null; // handled target
break;
}
}
// evaluate finally block
if(finallyBlock != null)
ret = finallyBlock.eval(callstack, interpreter);
// exception fell through, throw it upward...
if(target != null)
throw target;
if(ret instanceof ReturnControl)
return ret;
else
return Primitive.VOID;
}
// in org/gjt/sp/jedit/bsh/BSHAllocationExpression.java
public Object eval( CallStack callstack, Interpreter interpreter)
throws EvalError
{
// type is either a class name or a primitive type
SimpleNode type = (SimpleNode)jjtGetChild(0);
// args is either constructor arguments or array dimensions
SimpleNode args = (SimpleNode)jjtGetChild(1);
if ( type instanceof BSHAmbiguousName )
{
BSHAmbiguousName name = (BSHAmbiguousName)type;
if (args instanceof BSHArguments)
return objectAllocation(name, (BSHArguments)args,
callstack, interpreter );
else
return objectArrayAllocation(name, (BSHArrayDimensions)args,
callstack, interpreter );
}
else
return primitiveArrayAllocation((BSHPrimitiveType)type,
(BSHArrayDimensions)args, callstack, interpreter );
}
// in org/gjt/sp/jedit/bsh/BSHAllocationExpression.java
private Object objectAllocation(
BSHAmbiguousName nameNode, BSHArguments argumentsNode,
CallStack callstack, Interpreter interpreter
)
throws EvalError
{
NameSpace namespace = callstack.top();
Object[] args = argumentsNode.getArguments( callstack, interpreter );
if ( args == null)
throw new EvalError( "Null args in new.", this, callstack );
// Look for scripted class object
Object obj = nameNode.toObject(
callstack, interpreter, false/* force class*/ );
// Try regular class
obj = nameNode.toObject(
callstack, interpreter, true/*force class*/ );
Class type = null;
if ( obj instanceof ClassIdentifier )
type = ((ClassIdentifier)obj).getTargetClass();
else
throw new EvalError(
"Unknown class: "+nameNode.text, this, callstack );
// Is an inner class style object allocation
boolean hasBody = jjtGetNumChildren() > 2;
if ( hasBody )
{
BSHBlock body = (BSHBlock)jjtGetChild(2);
if ( type.isInterface() )
return constructWithInterfaceBody(
type, args, body, callstack, interpreter );
else
return constructWithClassBody(
type, args, body, callstack, interpreter );
} else
return constructObject( type, args, callstack );
}
// in org/gjt/sp/jedit/bsh/BSHAllocationExpression.java
private Object constructObject(
Class type, Object[] args, CallStack callstack )
throws EvalError
{
Object obj;
try {
obj = Reflect.constructObject( type, args );
} catch ( ReflectError e) {
throw new EvalError(
"Constructor error: " + e.getMessage(), this, callstack );
} catch(InvocationTargetException e) {
// No need to wrap this debug
Interpreter.debug("The constructor threw an exception:\n\t" +
e.getTargetException());
throw new TargetError(
"Object constructor", e.getTargetException(),
this, callstack, true);
}
String className = type.getName();
// Is it an inner class?
if ( className.indexOf("$") == -1 )
return obj;
// Temporary hack to support inner classes
// If the obj is a non-static inner class then import the context...
// This is not a sufficient emulation of inner classes.
// Replace this later...
// work through to class 'this'
This ths = callstack.top().getThis( null );
NameSpace instanceNameSpace =
Name.getClassNameSpace( ths.getNameSpace() );
// Change the parent (which was the class static) to the class instance
// We really need to check if we're a static inner class here first...
// but for some reason Java won't show the static modifier on our
// fake inner classes... could generate a flag field.
if ( instanceNameSpace != null
&& className.startsWith( instanceNameSpace.getName() +"$")
)
{
try {
ClassGenerator.getClassGenerator().setInstanceNameSpaceParent(
obj, className, instanceNameSpace );
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
}
return obj;
}
// in org/gjt/sp/jedit/bsh/BSHAllocationExpression.java
private Object constructWithClassBody(
Class type, Object[] args, BSHBlock block,
CallStack callstack, Interpreter interpreter )
throws EvalError
{
String name = callstack.top().getName() + "$" + (++innerClassCount);
Modifiers modifiers = new Modifiers();
modifiers.addModifier( Modifiers.CLASS, "public" );
Class clas;
try {
clas = ClassGenerator.getClassGenerator() .generateClass(
name, modifiers, null/*interfaces*/, type/*superClass*/,
block, false/*isInterface*/, callstack, interpreter );
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
try {
return Reflect.constructObject( clas, args );
} catch ( Exception e ) {
if ( e instanceof InvocationTargetException )
e = (Exception)((InvocationTargetException)e)
.getTargetException();
throw new EvalError(
"Error constructing inner class instance: "+e, this, callstack
);
}
}
// in org/gjt/sp/jedit/bsh/BSHAllocationExpression.java
private Object constructWithInterfaceBody(
Class type, Object[] args, BSHBlock body,
CallStack callstack, Interpreter interpreter )
throws EvalError
{
NameSpace namespace = callstack.top();
NameSpace local = new NameSpace(namespace, "AnonymousBlock");
callstack.push(local);
body.eval( callstack, interpreter, true/*overrideNamespace*/ );
callstack.pop();
// statical import fields from the interface so that code inside
// can refer to the fields directly (e.g. HEIGHT)
local.importStatic( type );
try {
return local.getThis(interpreter).getInterface( type );
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BSHAllocationExpression.java
private Object objectArrayAllocation(
BSHAmbiguousName nameNode, BSHArrayDimensions dimensionsNode,
CallStack callstack, Interpreter interpreter
)
throws EvalError
{
NameSpace namespace = callstack.top();
Class type = nameNode.toClass( callstack, interpreter );
if ( type == null )
throw new EvalError( "Class " + nameNode.getName(namespace)
+ " not found.", this, callstack );
return arrayAllocation( dimensionsNode, type, callstack, interpreter );
}
// in org/gjt/sp/jedit/bsh/BSHAllocationExpression.java
private Object primitiveArrayAllocation(
BSHPrimitiveType typeNode, BSHArrayDimensions dimensionsNode,
CallStack callstack, Interpreter interpreter
)
throws EvalError
{
Class type = typeNode.getType();
return arrayAllocation( dimensionsNode, type, callstack, interpreter );
}
// in org/gjt/sp/jedit/bsh/BSHAllocationExpression.java
private Object arrayAllocation(
BSHArrayDimensions dimensionsNode, Class type,
CallStack callstack, Interpreter interpreter )
throws EvalError
{
/*
dimensionsNode can return either a fully intialized array or VOID.
when VOID the prescribed array dimensions (defined and undefined)
are contained in the node.
*/
Object result = dimensionsNode.eval( type, callstack, interpreter );
if ( result != Primitive.VOID )
return result;
else
return arrayNewInstance( type, dimensionsNode, callstack );
}
// in org/gjt/sp/jedit/bsh/BSHAllocationExpression.java
private Object arrayNewInstance(
Class type, BSHArrayDimensions dimensionsNode, CallStack callstack )
throws EvalError
{
if ( dimensionsNode.numUndefinedDims > 0 )
{
Object proto = Array.newInstance(
type, new int [dimensionsNode.numUndefinedDims] ); // zeros
type = proto.getClass();
}
try {
return Array.newInstance(
type, dimensionsNode.definedDimensions);
} catch( NegativeArraySizeException e1 ) {
throw new TargetError( e1, this, callstack );
} catch( Exception e ) {
throw new EvalError("Can't construct primitive array: " +
e.getMessage(), this, callstack);
}
}
// in org/gjt/sp/jedit/bsh/Name.java
public Object invokeMethod(
Interpreter interpreter, Object[] args, CallStack callstack,
SimpleNode callerInfo
)
throws UtilEvalError, EvalError, ReflectError, InvocationTargetException
{
String methodName = Name.suffix(value, 1);
BshClassManager bcm = interpreter.getClassManager();
NameSpace namespace = callstack.top();
// Optimization - If classOfStaticMethod is set then we have already
// been here and determined that this is a static method invocation.
// Note: maybe factor this out with path below... clean up.
if ( classOfStaticMethod != null )
{
return Reflect.invokeStaticMethod(
bcm, classOfStaticMethod, methodName, args );
}
if ( !Name.isCompound(value) )
return invokeLocalMethod(
interpreter, args, callstack, callerInfo );
// Note: if we want methods declared inside blocks to be accessible via
// this.methodname() inside the block we could handle it here as a
// special case. See also resolveThisFieldReference() special handling
// for BlockNameSpace case. They currently work via the direct name
// e.g. methodName().
String prefix = Name.prefix(value);
// Superclass method invocation? (e.g. super.foo())
if ( prefix.equals("super") && Name.countParts(value) == 2 )
{
// Allow getThis() to work through block namespaces first
This ths = namespace.getThis( interpreter );
NameSpace thisNameSpace = ths.getNameSpace();
NameSpace classNameSpace = getClassNameSpace( thisNameSpace );
if ( classNameSpace != null )
{
Object instance = classNameSpace.getClassInstance();
return ClassGenerator.getClassGenerator()
.invokeSuperclassMethod( bcm, instance, methodName, args );
}
}
// Find target object or class identifier
Name targetName = namespace.getNameResolver( prefix );
Object obj = targetName.toObject( callstack, interpreter );
if ( obj == Primitive.VOID )
throw new UtilEvalError( "Attempt to resolve method: "+methodName
+"() on undefined variable or class name: "+targetName);
// if we've got an object, resolve the method
if ( !(obj instanceof ClassIdentifier) ) {
if (obj instanceof Primitive) {
if (obj == Primitive.NULL)
throw new UtilTargetError( new NullPointerException(
"Null Pointer in Method Invocation" ) );
// some other primitive
// should avoid calling methods on primitive, as we do
// in Name (can't treat primitive like an object message)
// but the hole is useful right now.
if ( Interpreter.DEBUG )
interpreter.debug(
"Attempt to access method on primitive..."
+ " allowing bsh.Primitive to peek through for debugging");
}
// found an object and it's not an undefined variable
return Reflect.invokeObjectMethod(
obj, methodName, args, interpreter, callstack, callerInfo );
}
// It's a class
// try static method
if ( Interpreter.DEBUG )
Interpreter.debug("invokeMethod: trying static - " + targetName);
Class clas = ((ClassIdentifier)obj).getTargetClass();
// cache the fact that this is a static method invocation on this class
classOfStaticMethod = clas;
if ( clas != null )
return Reflect.invokeStaticMethod( bcm, clas, methodName, args );
// return null; ???
throw new UtilEvalError("invokeMethod: unknown target: " + targetName);
}
// in org/gjt/sp/jedit/bsh/Name.java
private Object invokeLocalMethod(
Interpreter interpreter, Object[] args, CallStack callstack,
SimpleNode callerInfo
)
throws EvalError/*, ReflectError, InvocationTargetException*/
{
if ( Interpreter.DEBUG )
Interpreter.debug( "invokeLocalMethod: " + value );
if ( interpreter == null )
throw new InterpreterError(
"invokeLocalMethod: interpreter = null");
String commandName = value;
Class [] argTypes = Types.getTypes( args );
// Check for existing method
BshMethod meth = null;
try {
meth = namespace.getMethod( commandName, argTypes );
} catch ( UtilEvalError e ) {
throw e.toEvalError(
"Local method invocation", callerInfo, callstack );
}
// If defined, invoke it
if ( meth != null )
return meth.invoke( args, interpreter, callstack, callerInfo );
BshClassManager bcm = interpreter.getClassManager();
// Look for a BeanShell command
Object commandObject;
try {
commandObject = namespace.getCommand(
commandName, argTypes, interpreter );
} catch ( UtilEvalError e ) {
throw e.toEvalError("Error loading command: ",
callerInfo, callstack );
}
// should try to print usage here if nothing found
if ( commandObject == null )
{
// Look for a default invoke() handler method in the namespace
// Note: this code duplicates that in This.java... should it?
// Call on 'This' can never be a command
BshMethod invokeMethod = null;
try {
invokeMethod = namespace.getMethod(
"invoke", new Class [] { null, null } );
} catch ( UtilEvalError e ) {
throw e.toEvalError(
"Local method invocation", callerInfo, callstack );
}
if ( invokeMethod != null )
return invokeMethod.invoke(
new Object [] { commandName, args },
interpreter, callstack, callerInfo );
throw new EvalError( "Command not found: "
+StringUtil.methodString( commandName, argTypes ),
callerInfo, callstack );
}
if ( commandObject instanceof BshMethod )
return ((BshMethod)commandObject).invoke(
args, interpreter, callstack, callerInfo );
if ( commandObject instanceof Class )
try {
return Reflect.invokeCompiledCommand(
((Class)commandObject), args, interpreter, callstack );
} catch ( UtilEvalError e ) {
throw e.toEvalError("Error invoking compiled command: ",
callerInfo, callstack );
}
throw new InterpreterError("invalid command type");
}
// in org/gjt/sp/jedit/bsh/BSHPackageDeclaration.java
public Object eval( CallStack callstack, Interpreter interpreter )
throws EvalError
{
BSHAmbiguousName name = (BSHAmbiguousName)jjtGetChild(0);
NameSpace namespace = callstack.top();
namespace.setPackage( name.text );
// import the package we're in by default...
namespace.importPackage( name.text );
return Primitive.VOID;
}
// in org/gjt/sp/jedit/bsh/BSHThrowStatement.java
public Object eval( CallStack callstack, Interpreter interpreter)
throws EvalError
{
Object obj = ((SimpleNode)jjtGetChild(0)).eval(callstack, interpreter);
// need to loosen this to any throwable... do we need to handle
// that in interpreter somewhere? check first...
if(!(obj instanceof Exception))
throw new EvalError("Expression in 'throw' must be Exception type",
this, callstack );
// wrap the exception in a TargetException to propogate it up
throw new TargetError( (Exception)obj, this, callstack );
}
// in org/gjt/sp/jedit/bsh/BSHBinaryExpression.java
public Object eval( CallStack callstack, Interpreter interpreter)
throws EvalError
{
Object lhs = ((SimpleNode)jjtGetChild(0)).eval(callstack, interpreter);
/*
Doing instanceof? Next node is a type.
*/
if (kind == INSTANCEOF)
{
// null object ref is not instance of any type
if ( lhs == Primitive.NULL )
return new Primitive(false);
Class rhs = ((BSHType)jjtGetChild(1)).getType(
callstack, interpreter );
/*
// primitive (number or void) cannot be tested for instanceof
if (lhs instanceof Primitive)
throw new EvalError("Cannot be instance of primitive type." );
*/
/*
Primitive (number or void) is not normally an instanceof
anything. But for internal use we'll test true for the
bsh.Primitive class.
i.e. (5 instanceof bsh.Primitive) will be true
*/
if ( lhs instanceof Primitive )
if ( rhs == org.gjt.sp.jedit.bsh.Primitive.class )
return new Primitive(true);
else
return new Primitive(false);
// General case - performe the instanceof based on assignability
boolean ret = Types.isJavaBaseAssignable( rhs, lhs.getClass() );
return new Primitive(ret);
}
// The following two boolean checks were tacked on.
// This could probably be smoothed out.
/*
Look ahead and short circuit evaluation of the rhs if:
we're a boolean AND and the lhs is false.
*/
if ( kind == BOOL_AND || kind == BOOL_ANDX ) {
Object obj = lhs;
if ( isPrimitiveValue(lhs) )
obj = ((Primitive)lhs).getValue();
if ( obj instanceof Boolean &&
( ((Boolean)obj).booleanValue() == false ) )
return new Primitive(false);
}
/*
Look ahead and short circuit evaluation of the rhs if:
we're a boolean AND and the lhs is false.
*/
if ( kind == BOOL_OR || kind == BOOL_ORX ) {
Object obj = lhs;
if ( isPrimitiveValue(lhs) )
obj = ((Primitive)lhs).getValue();
if ( obj instanceof Boolean &&
( ((Boolean)obj).booleanValue() == true ) )
return new Primitive(true);
}
// end stuff that was tacked on for boolean short-circuiting.
/*
Are both the lhs and rhs either wrappers or primitive values?
do binary op
*/
boolean isLhsWrapper = isWrapper( lhs );
Object rhs = ((SimpleNode)jjtGetChild(1)).eval(callstack, interpreter);
boolean isRhsWrapper = isWrapper( rhs );
if (
( isLhsWrapper || isPrimitiveValue( lhs ) )
&& ( isRhsWrapper || isPrimitiveValue( rhs ) )
)
{
// Special case for EQ on two wrapper objects
if ( (isLhsWrapper && isRhsWrapper && kind == EQ))
{
/*
Don't auto-unwrap wrappers (preserve identity semantics)
FALL THROUGH TO OBJECT OPERATIONS BELOW.
*/
} else
try {
return Primitive.binaryOperation(lhs, rhs, kind);
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
}
/*
Doing the following makes it hard to use untyped vars...
e.g. if ( arg == null ) ...what if arg is a primitive?
The answer is that we should test only if the var is typed...?
need to get that info here...
else
{
// Do we have a mixture of primitive values and non-primitives ?
// (primitiveValue = not null, not void)
int primCount = 0;
if ( isPrimitiveValue( lhs ) )
++primCount;
if ( isPrimitiveValue( rhs ) )
++primCount;
if ( primCount > 1 )
// both primitive types, should have been handled above
throw new InterpreterError("should not be here");
else
if ( primCount == 1 )
// mixture of one and the other
throw new EvalError("Operator: '" + tokenImage[kind]
+"' inappropriate for object / primitive combination.",
this, callstack );
// else fall through to handle both non-primitive types
// end check for primitive and non-primitive mix
}
*/
/*
Treat lhs and rhs as arbitrary objects and do the operation.
(including NULL and VOID represented by their Primitive types)
*/
//System.out.println("binary op arbitrary obj: {"+lhs+"}, {"+rhs+"}");
switch(kind)
{
case EQ:
return new Primitive((lhs == rhs));
case NE:
return new Primitive((lhs != rhs));
case PLUS:
if(lhs instanceof String || rhs instanceof String)
return lhs.toString() + rhs.toString();
// FALL THROUGH TO DEFAULT CASE!!!
default:
if(lhs instanceof Primitive || rhs instanceof Primitive)
if ( lhs == Primitive.VOID || rhs == Primitive.VOID )
throw new EvalError(
"illegal use of undefined variable, class, or 'void' literal",
this, callstack );
else
if ( lhs == Primitive.NULL || rhs == Primitive.NULL )
throw new EvalError(
"illegal use of null value or 'null' literal", this, callstack);
throw new EvalError("Operator: '" + tokenImage[kind] +
"' inappropriate for objects", this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BSHIfStatement.java
public Object eval(CallStack callstack, Interpreter interpreter)
throws EvalError
{
Object ret = null;
if( evaluateCondition(
(SimpleNode)jjtGetChild(0), callstack, interpreter ) )
ret = ((SimpleNode)jjtGetChild(1)).eval(callstack, interpreter);
else
if(jjtGetNumChildren() > 2)
ret = ((SimpleNode)jjtGetChild(2)).eval(callstack, interpreter);
if(ret instanceof ReturnControl)
return ret;
else
return Primitive.VOID;
}
// in org/gjt/sp/jedit/bsh/BSHIfStatement.java
public static boolean evaluateCondition(
SimpleNode condExp, CallStack callstack, Interpreter interpreter)
throws EvalError
{
Object obj = condExp.eval(callstack, interpreter);
if(obj instanceof Primitive) {
if ( obj == Primitive.VOID )
throw new EvalError("Condition evaluates to void type",
condExp, callstack );
obj = ((Primitive)obj).getValue();
}
if(obj instanceof Boolean)
return ((Boolean)obj).booleanValue();
else
throw new EvalError(
"Condition must evaluate to a Boolean or boolean.",
condExp, callstack );
}
// in org/gjt/sp/jedit/bsh/BSHSwitchStatement.java
public Object eval( CallStack callstack, Interpreter interpreter )
throws EvalError
{
int numchild = jjtGetNumChildren();
int child = 0;
SimpleNode switchExp = ((SimpleNode)jjtGetChild(child++));
Object switchVal = switchExp.eval( callstack, interpreter );
/*
Note: this could be made clearer by adding an inner class for the
cases and an object context for the child traversal.
*/
// first label
BSHSwitchLabel label;
Object node;
ReturnControl returnControl=null;
// get the first label
if ( child >= numchild )
throw new EvalError("Empty switch statement.", this, callstack );
label = ((BSHSwitchLabel)jjtGetChild(child++));
// while more labels or blocks and haven't hit return control
while ( child < numchild && returnControl == null )
{
// if label is default or equals switchVal
if ( label.isDefault
|| primitiveEquals(
switchVal, label.eval( callstack, interpreter ),
callstack, switchExp )
)
{
// execute nodes, skipping labels, until a break or return
while ( child < numchild )
{
node = jjtGetChild(child++);
if ( node instanceof BSHSwitchLabel )
continue;
// eval it
Object value =
((SimpleNode)node).eval( callstack, interpreter );
// should check to disallow continue here?
if ( value instanceof ReturnControl ) {
returnControl = (ReturnControl)value;
break;
}
}
} else
{
// skip nodes until next label
while ( child < numchild )
{
node = jjtGetChild(child++);
if ( node instanceof BSHSwitchLabel ) {
label = (BSHSwitchLabel)node;
break;
}
}
}
}
if ( returnControl != null && returnControl.kind == RETURN )
return returnControl;
else
return Primitive.VOID;
}
// in org/gjt/sp/jedit/bsh/BSHSwitchStatement.java
private boolean primitiveEquals(
Object switchVal, Object targetVal,
CallStack callstack, SimpleNode switchExp )
throws EvalError
{
if ( switchVal instanceof Primitive || targetVal instanceof Primitive )
try {
// binaryOperation can return Primitive or wrapper type
Object result = Primitive.binaryOperation(
switchVal, targetVal, ParserConstants.EQ );
result = Primitive.unwrap( result );
return result.equals( Boolean.TRUE );
} catch ( UtilEvalError e ) {
throw e.toEvalError(
"Switch value: "+switchExp.getText()+": ",
this, callstack );
}
else
return switchVal.equals( targetVal );
}
// in org/gjt/sp/jedit/bsh/BSHBlock.java
public Object eval( CallStack callstack, Interpreter interpreter)
throws EvalError
{
return eval( callstack, interpreter, false );
}
// in org/gjt/sp/jedit/bsh/BSHBlock.java
public Object eval(
CallStack callstack, Interpreter interpreter,
boolean overrideNamespace )
throws EvalError
{
Object syncValue = null;
if ( isSynchronized )
{
// First node is the expression on which to sync
SimpleNode exp = ((SimpleNode)jjtGetChild(0));
syncValue = exp.eval(callstack, interpreter);
}
Object ret;
if ( isSynchronized ) // Do the actual synchronization
synchronized( syncValue )
{
ret = evalBlock(
callstack, interpreter, overrideNamespace, null/*filter*/);
}
else
ret = evalBlock(
callstack, interpreter, overrideNamespace, null/*filter*/ );
return ret;
}
// in org/gjt/sp/jedit/bsh/BSHBlock.java
Object evalBlock(
CallStack callstack, Interpreter interpreter,
boolean overrideNamespace, NodeFilter nodeFilter )
throws EvalError
{
Object ret = Primitive.VOID;
NameSpace enclosingNameSpace = null;
if ( !overrideNamespace )
{
enclosingNameSpace= callstack.top();
BlockNameSpace bodyNameSpace =
new BlockNameSpace( enclosingNameSpace );
callstack.swap( bodyNameSpace );
}
int startChild = isSynchronized ? 1 : 0;
int numChildren = jjtGetNumChildren();
try {
/*
Evaluate block in two passes:
First do class declarations then do everything else.
*/
for(int i=startChild; i<numChildren; i++)
{
SimpleNode node = ((SimpleNode)jjtGetChild(i));
if ( nodeFilter != null && !nodeFilter.isVisible( node ) )
continue;
if ( node instanceof BSHClassDeclaration )
node.eval( callstack, interpreter );
}
for(int i=startChild; i<numChildren; i++)
{
SimpleNode node = ((SimpleNode)jjtGetChild(i));
if ( node instanceof BSHClassDeclaration )
continue;
// filter nodes
if ( nodeFilter != null && !nodeFilter.isVisible( node ) )
continue;
ret = node.eval( callstack, interpreter );
// statement or embedded block evaluated a return statement
if ( ret instanceof ReturnControl )
break;
}
} finally {
// make sure we put the namespace back when we leave.
if ( !overrideNamespace )
callstack.swap( enclosingNameSpace );
}
return ret;
}
// in org/gjt/sp/jedit/bsh/BSHImportDeclaration.java
public Object eval( CallStack callstack, Interpreter interpreter)
throws EvalError
{
NameSpace namespace = callstack.top();
if ( superImport )
try {
namespace.doSuperImport();
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
else
{
if ( staticImport )
{
if ( importPackage )
{
Class clas = ((BSHAmbiguousName)jjtGetChild(0)).toClass(
callstack, interpreter );
namespace.importStatic( clas );
} else
throw new EvalError(
"static field imports not supported yet",
this, callstack );
} else
{
String name = ((BSHAmbiguousName)jjtGetChild(0)).text;
if ( importPackage )
namespace.importPackage(name);
else
namespace.importClass(name);
}
}
return Primitive.VOID;
}
// in org/gjt/sp/jedit/bsh/NameSpace.java
public Object invokeMethod(
String methodName, Object [] args, Interpreter interpreter )
throws EvalError
{
return invokeMethod(
methodName, args, interpreter, null, null );
}
// in org/gjt/sp/jedit/bsh/NameSpace.java
public Object invokeMethod(
String methodName, Object [] args, Interpreter interpreter,
CallStack callstack, SimpleNode callerInfo )
throws EvalError
{
return getThis( interpreter ).invokeMethod(
methodName, args, interpreter, callstack, callerInfo,
false/*declaredOnly*/ );
}
// in org/gjt/sp/jedit/bsh/BSHReturnStatement.java
public Object eval(CallStack callstack, Interpreter interpreter)
throws EvalError
{
Object value;
if(jjtGetNumChildren() > 0)
value = ((SimpleNode)jjtGetChild(0)).eval(callstack, interpreter);
else
value = Primitive.VOID;
return new ReturnControl( kind, value, this );
}
// in org/gjt/sp/jedit/bsh/EvalError.java
public void reThrow( String msg )
throws EvalError
{
prependMessage( msg );
throw this;
}
// in org/gjt/sp/jedit/bsh/BSHTernaryExpression.java
public Object eval( CallStack callstack, Interpreter interpreter)
throws EvalError
{
SimpleNode
cond = (SimpleNode)jjtGetChild(0),
evalTrue = (SimpleNode)jjtGetChild(1),
evalFalse = (SimpleNode)jjtGetChild(2);
if ( BSHIfStatement.evaluateCondition( cond, callstack, interpreter ) )
return evalTrue.eval( callstack, interpreter );
else
return evalFalse.eval( callstack, interpreter );
}
// in org/gjt/sp/jedit/bsh/BSHArrayDimensions.java
public Object eval(
Class type, CallStack callstack, Interpreter interpreter )
throws EvalError
{
if ( Interpreter.DEBUG ) Interpreter.debug("array base type = "+type);
baseType = type;
return eval( callstack, interpreter );
}
// in org/gjt/sp/jedit/bsh/BSHArrayDimensions.java
public Object eval( CallStack callstack, Interpreter interpreter )
throws EvalError
{
SimpleNode child = (SimpleNode)jjtGetChild(0);
/*
Child is array initializer. Evaluate it and fill in the
dimensions it returns. Initialized arrays are always fully defined
(no undefined dimensions to worry about).
The syntax uses the undefinedDimension count.
e.g. int [][] { 1, 2 };
*/
if (child instanceof BSHArrayInitializer)
{
if ( baseType == null )
throw new EvalError(
"Internal Array Eval err: unknown base type",
this, callstack );
Object initValue = ((BSHArrayInitializer)child).eval(
baseType, numUndefinedDims, callstack, interpreter);
Class arrayClass = initValue.getClass();
int actualDimensions = Reflect.getArrayDimensions(arrayClass);
definedDimensions = new int[ actualDimensions ];
// Compare with number of dimensions actually created with the
// number specified (syntax uses the undefined ones here)
if ( definedDimensions.length != numUndefinedDims )
throw new EvalError(
"Incompatible initializer. Allocation calls for a " +
numUndefinedDims+ " dimensional array, but initializer is a " +
actualDimensions + " dimensional array", this, callstack );
// fill in definedDimensions [] lengths
Object arraySlice = initValue;
for ( int i = 0; i < definedDimensions.length; i++ ) {
definedDimensions[i] = Array.getLength( arraySlice );
if ( definedDimensions[i] > 0 )
arraySlice = Array.get(arraySlice, 0);
}
return initValue;
}
else
// Evaluate the defined dimensions of the array
{
definedDimensions = new int[ numDefinedDims ];
for(int i = 0; i < numDefinedDims; i++)
{
try {
Object length = ((SimpleNode)jjtGetChild(i)).eval(
callstack, interpreter);
definedDimensions[i] = ((Primitive)length).intValue();
}
catch(Exception e)
{
throw new EvalError(
"Array index: " + i +
" does not evaluate to an integer", this, callstack );
}
}
}
return Primitive.VOID;
}
// in org/gjt/sp/jedit/bsh/BSHMethodDeclaration.java
Class evalReturnType( CallStack callstack, Interpreter interpreter )
throws EvalError
{
insureNodesParsed();
if ( returnTypeNode != null )
return returnTypeNode.evalReturnType( callstack, interpreter );
else
return null;
}
// in org/gjt/sp/jedit/bsh/BSHMethodDeclaration.java
public Object eval( CallStack callstack, Interpreter interpreter )
throws EvalError
{
returnType = evalReturnType( callstack, interpreter );
evalNodes( callstack, interpreter );
// Install an *instance* of this method in the namespace.
// See notes in BshMethod
// This is not good...
// need a way to update eval without re-installing...
// so that we can re-eval params, etc. when classloader changes
// look into this
NameSpace namespace = callstack.top();
BshMethod bshMethod = new BshMethod( this, namespace, modifiers );
try {
namespace.setMethod( name, bshMethod );
} catch ( UtilEvalError e ) {
throw e.toEvalError(this,callstack);
}
return Primitive.VOID;
}
// in org/gjt/sp/jedit/bsh/BSHMethodDeclaration.java
private void evalNodes( CallStack callstack, Interpreter interpreter )
throws EvalError
{
insureNodesParsed();
// validate that the throws names are class names
for(int i=firstThrowsClause; i<numThrows+firstThrowsClause; i++)
((BSHAmbiguousName)jjtGetChild(i)).toClass(
callstack, interpreter );
paramsNode.eval( callstack, interpreter );
// if strictJava mode, check for loose parameters and return type
if ( interpreter.getStrictJava() )
{
for(int i=0; i<paramsNode.paramTypes.length; i++)
if ( paramsNode.paramTypes[i] == null )
// Warning: Null callstack here. Don't think we need
// a stack trace to indicate how we sourced the method.
throw new EvalError(
"(Strict Java Mode) Undeclared argument type, parameter: " +
paramsNode.getParamNames()[i] + " in method: "
+ name, this, null );
if ( returnType == null )
// Warning: Null callstack here. Don't think we need
// a stack trace to indicate how we sourced the method.
throw new EvalError(
"(Strict Java Mode) Undeclared return type for method: "
+ name, this, null );
}
}
// in org/gjt/sp/jedit/bsh/BSHFormalParameters.java
public Object eval( CallStack callstack, Interpreter interpreter )
throws EvalError
{
if ( paramTypes != null )
return paramTypes;
insureParsed();
Class [] paramTypes = new Class[numArgs];
for(int i=0; i<numArgs; i++)
{
BSHFormalParameter param = (BSHFormalParameter)jjtGetChild(i);
paramTypes[i] = (Class)param.eval( callstack, interpreter );
}
this.paramTypes = paramTypes;
return paramTypes;
}
// in org/gjt/sp/jedit/bsh/BSHMethodInvocation.java
public Object eval( CallStack callstack, Interpreter interpreter )
throws EvalError
{
NameSpace namespace = callstack.top();
BSHAmbiguousName nameNode = getNameNode();
// Do not evaluate methods this() or super() in class instance space
// (i.e. inside a constructor)
if ( namespace.getParent() != null && namespace.getParent().isClass
&& ( nameNode.text.equals("super") || nameNode.text.equals("this") )
)
return Primitive.VOID;
Name name = nameNode.getName(namespace);
Object[] args = getArgsNode().getArguments(callstack, interpreter);
// This try/catch block is replicated is BSHPrimarySuffix... need to
// factor out common functionality...
// Move to Reflect?
try {
return name.invokeMethod( interpreter, args, callstack, this);
} catch ( ReflectError e ) {
throw new EvalError(
"Error in method invocation: " + e.getMessage(),
this, callstack );
} catch ( InvocationTargetException e )
{
String msg = "Method Invocation "+name;
Throwable te = e.getTargetException();
/*
Try to squeltch the native code stack trace if the exception
was caused by a reflective call back into the bsh interpreter
(e.g. eval() or source()
*/
boolean isNative = true;
if ( te instanceof EvalError )
if ( te instanceof TargetError )
isNative = ((TargetError)te).inNativeCode();
else
isNative = false;
throw new TargetError( msg, te, this, callstack, isNative );
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BSHEnhancedForStatement.java
public Object eval( CallStack callstack , Interpreter interpreter )
throws EvalError
{
Class elementType = null;
SimpleNode expression, statement=null;
NameSpace enclosingNameSpace = callstack.top();
SimpleNode firstNode =((SimpleNode)jjtGetChild(0));
int nodeCount = jjtGetNumChildren();
if ( firstNode instanceof BSHType )
{
elementType=((BSHType)firstNode).getType( callstack, interpreter );
expression=((SimpleNode)jjtGetChild(1));
if ( nodeCount>2 )
statement=((SimpleNode)jjtGetChild(2));
} else
{
expression=firstNode;
if ( nodeCount>1 )
statement=((SimpleNode)jjtGetChild(1));
}
BlockNameSpace eachNameSpace = new BlockNameSpace( enclosingNameSpace );
callstack.swap( eachNameSpace );
final Object iteratee = expression.eval( callstack, interpreter );
if ( iteratee == Primitive.NULL )
throw new EvalError("The collection, array, map, iterator, or " +
"enumeration portion of a for statement cannot be null.",
this, callstack );
CollectionManager cm = CollectionManager.getCollectionManager();
if ( !cm.isBshIterable( iteratee ) )
throw new EvalError("Can't iterate over type: "
+iteratee.getClass(), this, callstack );
BshIterator iterator = cm.getBshIterator( iteratee );
Object returnControl = Primitive.VOID;
while( iterator.hasNext() )
{
try {
if ( elementType != null )
eachNameSpace.setTypedVariable(
varName/*name*/, elementType/*type*/,
iterator.next()/*value*/, new Modifiers()/*none*/ );
else
eachNameSpace.setVariable( varName, iterator.next(), false );
} catch ( UtilEvalError e ) {
throw e.toEvalError(
"for loop iterator variable:"+ varName, this, callstack );
}
boolean breakout = false; // switch eats a multi-level break here?
if ( statement != null ) // not empty statement
{
Object ret = statement.eval( callstack, interpreter );
if (ret instanceof ReturnControl)
{
switch(((ReturnControl)ret).kind)
{
case RETURN:
returnControl = ret;
breakout = true;
break;
case CONTINUE:
break;
case BREAK:
breakout = true;
break;
}
}
}
if (breakout)
break;
}
callstack.swap(enclosingNameSpace);
return returnControl;
}
// in org/gjt/sp/jedit/bsh/BSHAssignment.java
public Object eval(
CallStack callstack, Interpreter interpreter)
throws EvalError
{
BSHPrimaryExpression lhsNode =
(BSHPrimaryExpression)jjtGetChild(0);
if ( lhsNode == null )
throw new InterpreterError( "Error, null LHSnode" );
boolean strictJava = interpreter.getStrictJava();
LHS lhs = lhsNode.toLHS( callstack, interpreter);
if ( lhs == null )
throw new InterpreterError( "Error, null LHS" );
// For operator-assign operations save the lhs value before evaluating
// the rhs. This is correct Java behavior for postfix operations
// e.g. i=1; i+=i++; // should be 2 not 3
Object lhsValue = null;
if ( operator != ASSIGN ) // assign doesn't need the pre-value
try {
lhsValue = lhs.getValue();
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
SimpleNode rhsNode = (SimpleNode)jjtGetChild(1);
Object rhs;
// implement "blocks" foo = { };
// if ( rhsNode instanceof BSHBlock )
// rsh =
// else
rhs = rhsNode.eval(callstack, interpreter);
if ( rhs == Primitive.VOID )
throw new EvalError("Void assignment.", this, callstack );
try {
switch(operator)
{
case ASSIGN:
return lhs.assign( rhs, strictJava );
case PLUSASSIGN:
return lhs.assign(
operation(lhsValue, rhs, PLUS), strictJava );
case MINUSASSIGN:
return lhs.assign(
operation(lhsValue, rhs, MINUS), strictJava );
case STARASSIGN:
return lhs.assign(
operation(lhsValue, rhs, STAR), strictJava );
case SLASHASSIGN:
return lhs.assign(
operation(lhsValue, rhs, SLASH), strictJava );
case ANDASSIGN:
case ANDASSIGNX:
return lhs.assign(
operation(lhsValue, rhs, BIT_AND), strictJava );
case ORASSIGN:
case ORASSIGNX:
return lhs.assign(
operation(lhsValue, rhs, BIT_OR), strictJava );
case XORASSIGN:
return lhs.assign(
operation(lhsValue, rhs, XOR), strictJava );
case MODASSIGN:
return lhs.assign(
operation(lhsValue, rhs, MOD), strictJava );
case LSHIFTASSIGN:
case LSHIFTASSIGNX:
return lhs.assign(
operation(lhsValue, rhs, LSHIFT), strictJava );
case RSIGNEDSHIFTASSIGN:
case RSIGNEDSHIFTASSIGNX:
return lhs.assign(
operation(lhsValue, rhs, RSIGNEDSHIFT ), strictJava );
case RUNSIGNEDSHIFTASSIGN:
case RUNSIGNEDSHIFTASSIGNX:
return lhs.assign(
operation(lhsValue, rhs, RUNSIGNEDSHIFT),
strictJava );
default:
throw new InterpreterError(
"unimplemented operator in assignment BSH");
}
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BSHSwitchLabel.java
public Object eval(
CallStack callstack, Interpreter interpreter) throws EvalError
{
if ( isDefault )
return null; // should probably error
SimpleNode label = ((SimpleNode)jjtGetChild(0));
return label.eval( callstack, interpreter );
}
// in org/gjt/sp/jedit/bsh/BSHArrayInitializer.java
public Object eval( CallStack callstack, Interpreter interpreter )
throws EvalError
{
throw new EvalError( "Array initializer has no base type.",
this, callstack );
}
// in org/gjt/sp/jedit/bsh/BSHArrayInitializer.java
public Object eval( Class baseType, int dimensions,
CallStack callstack, Interpreter interpreter )
throws EvalError
{
int numInitializers = jjtGetNumChildren();
// allocate the array to store the initializers
int [] dima = new int [dimensions]; // description of the array
// The other dimensions default to zero and are assigned when
// the values are set.
dima[0] = numInitializers;
Object initializers = Array.newInstance( baseType, dima );
// Evaluate the initializers
for (int i = 0; i < numInitializers; i++)
{
SimpleNode node = (SimpleNode)jjtGetChild(i);
Object currentInitializer;
if ( node instanceof BSHArrayInitializer ) {
if ( dimensions < 2 )
throw new EvalError(
"Invalid Location for Intializer, position: "+i,
this, callstack );
currentInitializer =
((BSHArrayInitializer)node).eval(
baseType, dimensions-1, callstack, interpreter);
} else
currentInitializer = node.eval( callstack, interpreter);
if ( currentInitializer == Primitive.VOID )
throw new EvalError(
"Void in array initializer, position"+i, this, callstack );
// Determine if any conversion is necessary on the initializers.
//
// Quick test to see if conversions apply:
// If the dimensionality of the array is 1 then the elements of
// the initializer can be primitives or boxable types. If it is
// greater then the values must be array (object) types and there
// are currently no conversions that we do on those.
// If we have conversions on those in the future then we need to
// get the real base type here instead of the dimensionless one.
Object value = currentInitializer;
if ( dimensions == 1 )
{
// We do a bsh cast here. strictJava should be able to affect
// the cast there when we tighten control
try {
value = Types.castObject(
currentInitializer, baseType, Types.CAST );
} catch ( UtilEvalError e ) {
throw e.toEvalError(
"Error in array initializer", this, callstack );
}
// unwrap any primitive, map voids to null, etc.
value = Primitive.unwrap( value );
}
// store the value in the array
try {
Array.set(initializers, i, value);
} catch( IllegalArgumentException e ) {
Interpreter.debug("illegal arg"+e);
throwTypeError( baseType, currentInitializer, i, callstack );
} catch( ArrayStoreException e ) { // I think this can happen
Interpreter.debug("arraystore"+e);
throwTypeError( baseType, currentInitializer, i, callstack );
}
}
return initializers;
}
// in org/gjt/sp/jedit/bsh/BSHArrayInitializer.java
private void throwTypeError(
Class baseType, Object initializer, int argNum, CallStack callstack )
throws EvalError
{
String rhsType;
if (initializer instanceof Primitive)
rhsType =
((Primitive)initializer).getType().getName();
else
rhsType = Reflect.normalizeClassName(
initializer.getClass());
throw new EvalError ( "Incompatible type: " + rhsType
+" in initializer of array type: "+ baseType
+" at position: "+argNum, this, callstack );
}
// in org/gjt/sp/jedit/bsh/BSHClassDeclaration.java
public Object eval( CallStack callstack, Interpreter interpreter )
throws EvalError
{
int child = 0;
// resolve superclass if any
Class superClass = null;
if ( extend )
{
BSHAmbiguousName superNode = (BSHAmbiguousName)jjtGetChild(child++);
superClass = superNode.toClass( callstack, interpreter );
}
// Get interfaces
Class [] interfaces = new Class[numInterfaces];
for( int i=0; i<numInterfaces; i++) {
BSHAmbiguousName node = (BSHAmbiguousName)jjtGetChild(child++);
interfaces[i] = node.toClass(callstack, interpreter);
if ( !interfaces[i].isInterface() )
throw new EvalError(
"Type: "+node.text+" is not an interface!",
this, callstack );
}
BSHBlock block;
// Get the class body BSHBlock
if ( child < jjtGetNumChildren() )
block = (BSHBlock)jjtGetChild(child);
else
block = new BSHBlock( ParserTreeConstants.JJTBLOCK );
try {
return ClassGenerator.getClassGenerator().generateClass(
name, modifiers, interfaces, superClass, block, isInterface,
callstack, interpreter );
} catch ( UtilEvalError e ) {
throw e.toEvalError( this, callstack );
}
}
// in org/gjt/sp/jedit/bsh/BSHFormalParameter.java
public Object eval( CallStack callstack, Interpreter interpreter)
throws EvalError
{
if ( jjtGetNumChildren() > 0 )
type = ((BSHType)jjtGetChild(0)).getType( callstack, interpreter );
else
type = UNTYPED;
return type;
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public Object source( String filename, NameSpace nameSpace )
throws FileNotFoundException, IOException, EvalError
{
File file = pathToFile( filename );
if ( Interpreter.DEBUG ) debug("Sourcing file: "+file);
Reader sourceIn = new BufferedReader( new FileReader(file) );
try {
return eval( sourceIn, nameSpace, filename );
} finally {
sourceIn.close();
}
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public Object source( String filename )
throws FileNotFoundException, IOException, EvalError
{
return source( filename, globalNameSpace );
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public Object eval(
Reader in, NameSpace nameSpace, String sourceFileInfo
/*, CallStack callstack */ )
throws EvalError
{
Object retVal = null;
if ( Interpreter.DEBUG ) debug("eval: nameSpace = "+nameSpace);
/*
Create non-interactive local interpreter for this namespace
with source from the input stream and out/err same as
this interpreter.
*/
Interpreter localInterpreter =
new Interpreter(
in, out, err, false, nameSpace, this, sourceFileInfo );
CallStack callstack = new CallStack( nameSpace );
boolean eof = false;
while(!eof)
{
SimpleNode node = null;
try
{
eof = localInterpreter.Line();
if (localInterpreter.get_jjtree().nodeArity() > 0)
{
node = (SimpleNode)localInterpreter.get_jjtree().rootNode();
// nodes remember from where they were sourced
node.setSourceFile( sourceFileInfo );
if ( TRACE )
println( "// " +node.getText() );
retVal = node.eval( callstack, localInterpreter );
// sanity check during development
if ( callstack.depth() > 1 )
throw new InterpreterError(
"Callstack growing: "+callstack);
if ( retVal instanceof ReturnControl ) {
retVal = ((ReturnControl)retVal).value;
break; // non-interactive, return control now
}
if ( localInterpreter.showResults
&& retVal != Primitive.VOID )
println("<" + retVal + ">");
}
} catch(ParseException e) {
/*
throw new EvalError(
"Sourced file: "+sourceFileInfo+" parser Error: "
+ e.getMessage( DEBUG ), node, callstack );
*/
if ( DEBUG )
// show extra "expecting..." info
error( e.getMessage(DEBUG) );
// add the source file info and throw again
e.setErrorSourceFile( sourceFileInfo );
throw e;
} catch ( InterpreterError e ) {
e.printStackTrace();
throw new EvalError(
"Sourced file: "+sourceFileInfo+" internal Error: "
+ e.getMessage(), node, callstack);
} catch ( TargetError e ) {
// failsafe, set the Line as the origin of the error.
if ( e.getNode()==null )
e.setNode( node );
e.reThrow("Sourced file: "+sourceFileInfo);
} catch ( EvalError e) {
if ( DEBUG)
e.printStackTrace();
// failsafe, set the Line as the origin of the error.
if ( e.getNode()==null )
e.setNode( node );
e.reThrow( "Sourced file: "+sourceFileInfo );
} catch ( Exception e) {
if ( DEBUG)
e.printStackTrace();
throw new EvalError(
"Sourced file: "+sourceFileInfo+" unknown error: "
+ e.getMessage(), node, callstack);
} catch(TokenMgrError e) {
throw new EvalError(
"Sourced file: "+sourceFileInfo+" Token Parsing Error: "
+ e.getMessage(), node, callstack );
} finally {
localInterpreter.get_jjtree().reset();
// reinit the callstack
if ( callstack.depth() > 1 ) {
callstack.clear();
callstack.push( nameSpace );
}
}
}
return Primitive.unwrap( retVal );
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public Object eval( Reader in ) throws EvalError
{
return eval( in, globalNameSpace, "eval stream" );
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public Object eval( String statements ) throws EvalError {
if ( Interpreter.DEBUG ) debug("eval(String): "+statements);
return eval(statements, globalNameSpace);
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public Object eval( String statements, NameSpace nameSpace )
throws EvalError
{
String s = ( statements.endsWith(";") ? statements : statements+";" );
return eval(
new StringReader(s), nameSpace,
"inline evaluation of: ``"+ showEvalString(s)+"''" );
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public Object get( String name ) throws EvalError {
try {
Object ret = globalNameSpace.get( name, this );
return Primitive.unwrap( ret );
} catch ( UtilEvalError e ) {
throw e.toEvalError( SimpleNode.JAVACODE, new CallStack() );
}
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public void set( String name, Object value )
throws EvalError
{
// map null to Primtive.NULL coming in...
if ( value == null )
value = Primitive.NULL;
CallStack callstack = new CallStack();
try {
if ( Name.isCompound( name ) )
{
LHS lhs = globalNameSpace.getNameResolver( name ).toLHS(
callstack, this );
lhs.assign( value, false );
} else // optimization for common case
globalNameSpace.setVariable( name, value, false );
} catch ( UtilEvalError e ) {
throw e.toEvalError( SimpleNode.JAVACODE, callstack );
}
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public void set(String name, long value) throws EvalError {
set(name, new Primitive(value));
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public void set(String name, int value) throws EvalError {
set(name, new Primitive(value));
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public void set(String name, double value) throws EvalError {
set(name, new Primitive(value));
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public void set(String name, float value) throws EvalError {
set(name, new Primitive(value));
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public void set(String name, boolean value) throws EvalError {
set(name, new Primitive(value));
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public void unset( String name )
throws EvalError
{
/*
We jump through some hoops here to handle arbitrary cases like
unset("bsh.foo");
*/
CallStack callstack = new CallStack();
try {
LHS lhs = globalNameSpace.getNameResolver( name ).toLHS(
callstack, this );
if ( lhs.type != LHS.VARIABLE )
throw new EvalError("Can't unset, not a variable: "+name,
SimpleNode.JAVACODE, new CallStack() );
//lhs.assign( null, false );
lhs.nameSpace.unsetVariable( name );
} catch ( UtilEvalError e ) {
throw new EvalError( e.getMessage(),
SimpleNode.JAVACODE, new CallStack() );
}
}
// in org/gjt/sp/jedit/bsh/Interpreter.java
public Object getInterface( Class interf ) throws EvalError
{
try {
return globalNameSpace.getThis( this ).getInterface( interf );
} catch ( UtilEvalError e ) {
throw e.toEvalError( SimpleNode.JAVACODE, new CallStack() );
}
}
// in org/gjt/sp/jedit/bsh/BSHVariableDeclarator.java
public Object eval(
BSHType typeNode, CallStack callstack, Interpreter interpreter)
throws EvalError
{
// null value means no value
Object value = null;
if ( jjtGetNumChildren() > 0 )
{
SimpleNode initializer = (SimpleNode)jjtGetChild(0);
/*
If we have type info and the child is an array initializer
pass it along... Else use the default eval style.
(This allows array initializer to handle the problem...
allowing for future enhancements in loosening types there).
*/
if ( (typeNode != null)
&& initializer instanceof BSHArrayInitializer
)
value = ((BSHArrayInitializer)initializer).eval(
typeNode.getBaseType(), typeNode.getArrayDims(),
callstack, interpreter);
else
value = initializer.eval( callstack, interpreter);
}
if ( value == Primitive.VOID )
throw new EvalError("Void initializer.", this, callstack );
return value;
}
// in org/gjt/sp/jedit/bsh/BSHReturnType.java
public Class evalReturnType(
CallStack callstack, Interpreter interpreter ) throws EvalError
{
if ( isVoid )
return Void.TYPE;
else
return getTypeNode().getType( callstack, interpreter );
}
// in org/gjt/sp/jedit/bsh/BSHStatementExpressionList.java
public Object eval(CallStack callstack, Interpreter interpreter)
throws EvalError
{
int n = jjtGetNumChildren();
for(int i=0; i<n; i++)
{
SimpleNode node = ((SimpleNode)jjtGetChild(i));
node.eval(callstack, interpreter);
}
return Primitive.VOID;
}