diff --git a/.classpath b/.classpath
index 23936f7..638e532 100644
--- a/.classpath
+++ b/.classpath
@@ -8,6 +8,6 @@
-
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c3e7b8f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+## class files
+classes/
+
+## auto-generate code from javacc/jjtree
+src/bsh/Parser.java
+src/bsh/ParserConstants.java
+src/bsh/ParserTokenManager.java
+src/bsh/ParserTreeConstants.java
+src/bsh/bsh.jj
+/bin/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..bdc988a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+This is the fork of [beanshell.org](http://www.beanshell.org/) called Beanshell2 which lived between 2011 and
+2014 at [code.google.com](https://code.google.com/p/beanshell2). The motivation of the fork was that the
+original author was no longer maintaining it.
+
+In 2015 the repository moved here when Google retired their code hosting site.
+
+The old version is again maintained at [github.com/beanshell](https://github.com/beanshell). There is some
+ongoing effort to integrate the changes and improvements made to the project there.
+
+The development branch here is [v2.1](https://github.com/pejobo/beanshell2/tree/v2.1), the latest 'released'
+version is
+[2.1.9](https://github.com/pejobo/beanshell2/raw/v2.1/dist/bsh-2.1.9.jar)
+which maps to commit
+[a70056fbe9727d8eadf8e21f3089cbe9f4f0913e](https://github.com/pejobo/beanshell2/commit/a70056fbe9727d8eadf8e21f3089cbe9f4f0913e).
+
+See [releases page](https://github.com/pejobo/beanshell2/dist/README.md) for more information.
+
+
diff --git a/build.xml b/build.xml
index 9dc2381..b7d31e3 100644
--- a/build.xml
+++ b/build.xml
@@ -18,7 +18,7 @@
-
+
diff --git a/dist/README.md b/dist/README.md
new file mode 100644
index 0000000..0312a48
--- /dev/null
+++ b/dist/README.md
@@ -0,0 +1,63 @@
+March 13, 2018
+
+* Update [2.1.9](https://github.com/pejobo/beanshell2/raw/v2.1/dist/bsh-2.1.9.jar)
+ * Drop of all remote code execution capabilities
+ * Restricting (de)serialization for security reasons (the serialization feature may be dropped in one of the next
+ releases) [#109](https://github.com/pejobo/beanshell2/issues/109)
+ * Ability to run with Java-9 and Java-10 with restricted module access (jvm flag _--illegal-access=deny_).
+ * Fixed a deadlock when pasting code into the graphical console
+ * [SHA512](https://raw.githubusercontent.com/pejobo/beanshell2/v2.1/dist/bsh-2.1.9.jar.sha512sum)
+
+Feb. 20, 2014
+
+* Update [2.1.8](https://github.com/pejobo/beanshell2/raw/5b925f056c7a4b192fcd7389c9362d4f43403f70/downloads/bsh-2.1.8.jar)
+ * fixes [#97](https://github.com/pejobo/beanshell2/issues/97),
+ [#98](https://github.com/pejobo/beanshell2/issues/98), and
+ [#99](https://github.com/pejobo/beanshell2/issues/99) - all variants of a regression introduced with
+ [#88](https://github.com/pejobo/beanshell2/issues/88)
+
+
+Nov. 6, 2013
+
+* Update 2.1.7
+ * Another hotfix when running beanshell in a security restricted environment
+ * Fix for finally block not executed when an exception is thrown in catch block - thanks to Lorenzo Cameroni for pointing this out and suggesting a fix
+
+
+Sep. 27, 2013
+
+* Update v2.1.6
+ * This release mainly fixes issues of running beanshell in a security restricted environment. This may break existing
+ scriptes which define classes with protected methods, constructors or fields or which access inherited protected
+ methods, constructors or fields. If your (script) code doesn't explicitly switch on the accessibility mode your
+ script code will break with this update.
+ To receive the old behaviour either call `bsh.Capabilities.setAccessibility(true)` in your java code or
+ `setAccessibility(true)` in your script code. This change was done to allow the usage of beanshell2 in security
+ restricted environments. See issue [#88](https://github.com/pejobo/beanshell2/issues/88) for code changes.
+ * New version number scheme (drop of b for build).
+
+
+Nov. 21, 2011
+
+* Update v2.1b5, fixing
+ * Do-while loop does not check condition on "continue" - issue [#57](https://github.com/pejobo/beanshell2/issues/57)
+ * Fixes when using JSR-223 an exception which is thrown for clause that shouldn't be evaluated - issue
+ [#60](https://github.com/pejobo/beanshell2/issues/60).
+
+Older downloads are still available at [code.google.com](https://code.google.com/archive/p/beanshell2/downloads)
+
+Other notable changes not mentioned above in contrast to the latest version available at
+[beanshell.org](http://www.beanshell.org) are:
+* The support for parsing of java files through the class loader has been dropped. It was considered more harmful than
+ helpful.
+* The support of Java-5 varargs.
+* Support for long string literals:
+ ```
+ xml = """
+
+ Beanshell2
+
+ """"
+ ```
+* Build-in [jsr-233](https://www.jcp.org/en/jsr/detail?id=223) support (_Scripting for the Java Platform_).
+
\ No newline at end of file
diff --git a/src/README.txt b/src/README.txt
deleted file mode 100644
index 6ccb9c8..0000000
--- a/src/README.txt
+++ /dev/null
@@ -1,7 +0,0 @@
--- This is BeanShell2 --
-
-BeanShell2 is a fork of http://www.beanshell.org/
-
-For update and documentation see http://code.google.com/p/beanshell2
-
-BeanShell2 is designed to work with versions of Java 1.5 and later
diff --git a/src/bsh/BSHLiteral.java b/src/bsh/BSHLiteral.java
index 06a60f3..b1d9743 100644
--- a/src/bsh/BSHLiteral.java
+++ b/src/bsh/BSHLiteral.java
@@ -85,8 +85,36 @@ private char getEscapeChar(char ch)
return ch;
}
+ public static String decode(String str) {
+ StringBuilder sb = new StringBuilder(str.length());
+ char[] chars = str.toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ if (i + 1 < chars.length && c == '\\' && chars[i + 1] == 'u') {
+ char cc = 0;
+ for (int j = 0; j < 4; j++) {
+ char ch = Character.toLowerCase(chars[i + 2 + j]);
+ if ('0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'f' || 'A' <= ch && ch <= 'F') {
+ cc |= (Character.digit(ch, 16) << (3 - j) * 4);
+ } else {
+ cc = 0;
+ break;
+ }
+ }
+ if (cc > 0) {
+ i += 5;
+ sb.append(cc);
+ continue;
+ }
+ }
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+
public void charSetup(String str)
{
+ str = decode(str);
char ch = str.charAt(0);
if(ch == '\\')
{
@@ -104,6 +132,7 @@ public void charSetup(String str)
void stringSetup(String str)
{
+ str = decode(str);
StringBuilder buffer = new StringBuilder();
int len = str.length();
for(int i = 0; i < len; i++)
diff --git a/src/bsh/BSHWhileStatement.java b/src/bsh/BSHWhileStatement.java
index 791f7b8..81a9cd8 100644
--- a/src/bsh/BSHWhileStatement.java
+++ b/src/bsh/BSHWhileStatement.java
@@ -31,68 +31,64 @@
* *
*****************************************************************************/
-
package bsh;
/**
- This class handles both while(){} statements and do{}while() statements.
+ * This class handles both {@code while} statements and {@code do..while} statements.
*/
-class BSHWhileStatement extends SimpleNode implements ParserConstants
-{
- public boolean isDoStatement;
+class BSHWhileStatement extends SimpleNode implements ParserConstants {
+
+ /**
+ * Set by Parser, default {@code false}
+ */
+ boolean isDoStatement;
- BSHWhileStatement(int id) { super(id); }
+ BSHWhileStatement(int id) {
+ super(id);
+ }
- public Object eval( CallStack callstack, Interpreter interpreter)
- throws EvalError
- {
+
+ 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;
+ final SimpleNode condExp;
+ final SimpleNode body;
if ( isDoStatement ) {
- condExp = (SimpleNode)jjtGetChild(1);
- body =(SimpleNode)jjtGetChild(0);
+ 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);
+ condExp = (SimpleNode) jjtGetChild(0);
+ if ( numChild > 1 ) {
+ body = (SimpleNode) jjtGetChild(1);
+ } else {
+ body = null;
+ }
}
boolean doOnceFlag = isDoStatement;
- while(
- doOnceFlag ||
- BSHIfStatement.evaluateCondition(condExp, callstack, interpreter )
- )
- {
- if ( body == null ) // no body?
- continue;
+ while (doOnceFlag || BSHIfStatement.evaluateCondition(condExp, callstack, interpreter)) {
+ doOnceFlag = false;
+ // no body?
+ if ( body == null ) {
+ continue;
+ }
Object ret = body.eval(callstack, interpreter);
-
- boolean breakout = false;
- if(ret instanceof ReturnControl)
- {
- switch(((ReturnControl)ret).kind )
- {
+ if (ret instanceof ReturnControl) {
+ switch(( (ReturnControl)ret).kind ) {
case RETURN:
return ret;
case CONTINUE:
- continue;
+ break;
case BREAK:
- breakout = true;
- break;
+ return Primitive.VOID;
}
}
- if(breakout)
- break;
-
- doOnceFlag = false;
}
-
return Primitive.VOID;
}
diff --git a/src/bsh/BshClassManager.java b/src/bsh/BshClassManager.java
index 253146b..079bc8d 100644
--- a/src/bsh/BshClassManager.java
+++ b/src/bsh/BshClassManager.java
@@ -35,6 +35,7 @@
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -185,8 +186,35 @@ public Class classForName( String name )
clas = plainClassForName( name );
} catch ( ClassNotFoundException e ) { /*ignore*/ }
+ // try scripted class
+ if ( clas == null && declaringInterpreter.getCompatibility() )
+ clas = loadSourceClass( name );
+
return clas;
}
+
+ // Move me to classpath/ClassManagerImpl???
+ protected Class> loadSourceClass( final String name ) {
+ final String fileName = '/' + name.replace('.', '/') + ".java";
+ final InputStream in = getResourceAsStream( fileName );
+ if ( in == null ) {
+ return null;
+ }
+ try {
+ Interpreter.debug("Loading class from source file: " + fileName);
+ declaringInterpreter.eval( new InputStreamReader(in) );
+ } catch ( EvalError e ) {
+ if (Interpreter.DEBUG) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ return plainClassForName( name );
+ } catch ( final ClassNotFoundException e ) {
+ Interpreter.debug("Class not found in source file: " + name);
+ return null;
+ }
+ }
/**
Perform a plain Class.forName() or call the externally provided
diff --git a/src/bsh/BshScriptEngineFactory.java b/src/bsh/BshScriptEngineFactory.java
index 90cab3b..6ac06ae 100644
--- a/src/bsh/BshScriptEngineFactory.java
+++ b/src/bsh/BshScriptEngineFactory.java
@@ -8,11 +8,11 @@
public class BshScriptEngineFactory implements javax.script.ScriptEngineFactory {
// Begin impl ScriptEnginInfo
- final List extensions = Arrays.asList("bsh", "java");
+ final List extensions = Arrays.asList("bsh");
- final List mimeTypes = Arrays.asList("application/x-beanshell", "application/x-bsh", "application/x-java-source");
+ final List mimeTypes = Arrays.asList("application/x-beanshell", "application/x-bsh");
- final List names = Arrays.asList("beanshell", "bsh", "java");
+ final List names = Arrays.asList("beanshell", "bsh");
public String getEngineName() {
diff --git a/src/bsh/Capabilities.java b/src/bsh/Capabilities.java
index 9373720..a4b250a 100644
--- a/src/bsh/Capabilities.java
+++ b/src/bsh/Capabilities.java
@@ -33,6 +33,7 @@
package bsh;
+import java.lang.reflect.Field;
import java.util.Hashtable;
/**
@@ -80,6 +81,13 @@ public static void setAccessibility( boolean b )
// test basic access
try {
String.class.getDeclaredMethods();
+ try {
+ final Field field = Capabilities.class.getField("classes");
+ field.setAccessible(true);
+ field.setAccessible(false);
+ } catch (NoSuchFieldException e) {
+ // ignore
+ }
} catch ( SecurityException e ) {
throw new Unavailable("Accessibility unavailable: "+e);
}
diff --git a/src/bsh/ClassGeneratorUtil.java b/src/bsh/ClassGeneratorUtil.java
index 22adfe0..4d93e58 100644
--- a/src/bsh/ClassGeneratorUtil.java
+++ b/src/bsh/ClassGeneratorUtil.java
@@ -1000,7 +1000,7 @@ public static void initInstance(GeneratedClass instance, String className, Objec
if (e instanceof InvocationTargetException) {
e = (Exception) ((InvocationTargetException) e).getTargetException();
}
- throw new InterpreterError("Error in class initialization: " + e);
+ throw new InterpreterError("Error in class initialization.", e);
}
}
diff --git a/src/bsh/Interpreter.java b/src/bsh/Interpreter.java
index 5698744..93ce086 100644
--- a/src/bsh/Interpreter.java
+++ b/src/bsh/Interpreter.java
@@ -107,7 +107,7 @@ public class Interpreter
{
/* --- Begin static members --- */
- public static final String VERSION = "2.1b5";
+ public static final String VERSION = "2.2.0";
/*
Debug utils are static so that they are reachable by code that doesn't
necessarily have an interpreter reference (e.g. tracing in utils).
@@ -117,6 +117,7 @@ necessarily have an interpreter reference (e.g. tracing in utils).
turns it on or off.
*/
public static boolean DEBUG, TRACE, LOCALSCOPING;
+ public static boolean COMPATIBIILTY;
// This should be per instance
transient static PrintStream debug;
@@ -160,6 +161,13 @@ necessarily have an interpreter reference (e.g. tracing in utils).
/** Control the verbose printing of results for the show() command. */
private boolean showResults;
+ /**
+ * Compatibility mode. When {@code true} missing classes are tried to create from corresponding java source files.
+ * Default value is {@code false}, could be changed to {@code true} by setting the system property
+ * "bsh.compatibility" to "true".
+ */
+ private boolean compatibility = COMPATIBIILTY;
+
/* --- End instance data --- */
/**
@@ -1121,17 +1129,13 @@ public boolean getStrictJava() {
static void staticInit()
{
- /*
- Apparently in some environments you can't catch the security exception
- at all... e.g. as an applet in IE ... will probably have to work
- around
- */
try {
systemLineSeparator = System.getProperty("line.separator");
debug = System.err;
DEBUG = Boolean.getBoolean("debug");
TRACE = Boolean.getBoolean("trace");
LOCALSCOPING = Boolean.getBoolean("localscoping");
+ COMPATIBIILTY = Boolean.getBoolean("bsh.compatibility");
String outfilename = System.getProperty("outfile");
if ( outfilename != null )
redirectOutputToFile( outfilename );
@@ -1255,4 +1259,27 @@ public static void setShutdownOnExit(final boolean value) {
}
}
+
+ /**
+ * Compatibility mode. When {@code true} missing classes are tried to create from corresponding java source files.
+ * The Default value is {@code false}. This could be changed to {@code true} by setting the system property
+ * "bsh.compatibility" to "true".
+ *
+ * @see #setCompatibility(boolean)
+ */
+ public boolean getCompatibility() {
+ return compatibility;
+ }
+
+
+ /**
+ * Setting compatibility mode. When {@code true} missing classes are tried to create from corresponding java source
+ * files. The Default value is {@code false}. This could be changed to {@code true} by setting the system property
+ * "bsh.compatibility" to "true".
+ *
+ * @see #getCompatibility()
+ */
+ public void setCompatibility(final boolean value) {
+ compatibility = value;
+ }
}
\ No newline at end of file
diff --git a/src/bsh/JavaCharStream.java b/src/bsh/JavaCharStream.java
index 92064ea..f398b08 100644
--- a/src/bsh/JavaCharStream.java
+++ b/src/bsh/JavaCharStream.java
@@ -260,80 +260,9 @@ public char readChar() throws java.io.IOException
if (++bufpos == available)
AdjustBuffSize();
-
- if ((buffer[bufpos] = c = ReadByte()) == '\\')
- {
- UpdateLineColumn(c);
-
- int backSlashCnt = 1;
-
- for (;;) // Read all the backslashes
- {
- if (++bufpos == available)
- AdjustBuffSize();
-
- try
- {
- if ((buffer[bufpos] = c = ReadByte()) != '\\')
- {
- UpdateLineColumn(c);
- // found a non-backslash char.
- if ((c == 'u') && ((backSlashCnt & 1) == 1))
- {
- if (--bufpos < 0)
- bufpos = bufsize - 1;
-
- break;
- }
-
- backup(backSlashCnt);
- return '\\';
- }
- }
- catch(java.io.IOException e)
- {
- if (backSlashCnt > 1)
- backup(backSlashCnt);
-
- return '\\';
- }
-
- UpdateLineColumn(c);
- backSlashCnt++;
- }
-
- // Here, we have seen an odd number of backslash's followed by a 'u'
- try
- {
- while ((c = ReadByte()) == 'u')
- ++column;
-
- buffer[bufpos] = c = (char)(hexval(c) << 12 |
- hexval(ReadByte()) << 8 |
- hexval(ReadByte()) << 4 |
- hexval(ReadByte()));
-
- column += 4;
- }
- catch(java.io.IOException e)
- {
- throw new Error("Invalid escape character at line " + line +
- " column " + column + ".");
- }
-
- if (backSlashCnt == 1)
- return c;
- else
- {
- backup(backSlashCnt - 1);
- return '\\';
- }
- }
- else
- {
- UpdateLineColumn(c);
- return (c);
- }
+ buffer[bufpos] = c = ReadByte();
+ UpdateLineColumn(c);
+ return c;
}
/**
diff --git a/src/bsh/LHS.java b/src/bsh/LHS.java
index 1db674f..059c534 100644
--- a/src/bsh/LHS.java
+++ b/src/bsh/LHS.java
@@ -205,7 +205,7 @@ public Object assign( Object val, boolean strictJava )
((Primitive)val).getValue() : val;
// This should probably be in Reflect.java
- field.setAccessible(true);
+ Reflect.setAccessible(field);
field.set( object, fieldVal );
return val;
}
diff --git a/src/bsh/Name.java b/src/bsh/Name.java
index dbb50a9..22ce562 100644
--- a/src/bsh/Name.java
+++ b/src/bsh/Name.java
@@ -842,7 +842,8 @@ public Object invokeMethod(
if (obj == Primitive.NULL)
throw new UtilTargetError( new NullPointerException(
- "Null Pointer in Method Invocation" ) );
+ "Null Pointer in Method Invocation of " +methodName
+ +"() on variable: "+targetName) );
// some other primitive
// should avoid calling methods on primitive, as we do
diff --git a/src/bsh/NameSpace.java b/src/bsh/NameSpace.java
index 16bdef6..1ec8504 100644
--- a/src/bsh/NameSpace.java
+++ b/src/bsh/NameSpace.java
@@ -1216,7 +1216,8 @@ private Class classForName( String name )
protected void getAllNamesAux( List list )
{
list.addAll( variables.keySet() );
- list.addAll( methods.keySet() );
+ if ( methods != null )
+ list.addAll( methods.keySet() );
if ( parent != null )
parent.getAllNamesAux( list );
}
diff --git a/src/bsh/ParseException.java b/src/bsh/ParseException.java
index 6dba65a..66a755c 100644
--- a/src/bsh/ParseException.java
+++ b/src/bsh/ParseException.java
@@ -106,7 +106,7 @@ public ParseException() {
public ParseException(String message) {
// Begin BeanShell Modification - super constructor args
// null node, null callstack, ParseException knows where the error is.
- super( message, null, null );
+ super(message, null, null);
// End BeanShell Modification - super constructor args
specialConstructor = false;
}
@@ -123,6 +123,7 @@ public ParseException(String message,Throwable cause) {
* This variable determines which constructor was used to create
* this object and thereby affects the semantics of the
* "getMessage" method (see below).
+ * This is the same as "currentToken != null".
*/
protected boolean specialConstructor;
@@ -273,7 +274,25 @@ protected String add_escapes(String str) {
public int getErrorLineNumber()
{
- return currentToken.next.beginLine;
+ if (currentToken == null) {
+ String message = getMessage();
+ int index = message.indexOf(" at line ");
+ if (index > -1) {
+ message = message.substring(index + 9);
+ index = message.indexOf(',');
+ try {
+ if (index == -1) {
+ return Integer.parseInt(message);
+ }
+ return Integer.parseInt(message.substring(0, index));
+ } catch (NumberFormatException e) {
+ // ignore, we have no valid line information, just return -1 for now
+ }
+ }
+ return -1;
+ } else {
+ return currentToken.next.beginLine;
+ }
}
public String getErrorText() {
diff --git a/src/bsh/PreparsedScript.java b/src/bsh/PreparsedScript.java
index 5244bdf..b06d7ee 100644
--- a/src/bsh/PreparsedScript.java
+++ b/src/bsh/PreparsedScript.java
@@ -57,7 +57,8 @@ public Object invoke(final Map context) throws EvalError {
final BshMethod method = new BshMethod(_method.getName(), _method.getReturnType(), _method.getParameterNames(), _method.getParameterTypes(), _method.methodBody, nameSpace, _method.getModifiers());
for (final Map.Entry entry : context.entrySet()) {
try {
- nameSpace.setVariable(entry.getKey(), entry.getValue(), false);
+ final Object value = entry.getValue();
+ nameSpace.setVariable(entry.getKey(), value != null ? value : Primitive.NULL, false);
} catch (final UtilEvalError e) {
throw new EvalError("cannot set variable '" + entry.getKey() + '\'', null, null, e);
}
diff --git a/src/bsh/Primitive.java b/src/bsh/Primitive.java
index 965965b..20a52e1 100644
--- a/src/bsh/Primitive.java
+++ b/src/bsh/Primitive.java
@@ -756,6 +756,10 @@ static float floatUnaryOperation(Float F, int kind)
return operand;
case MINUS:
return -operand;
+ case INCR:
+ return operand + 1;
+ case DECR:
+ return operand - 1;
default:
throw new InterpreterError("bad float unaryOperation");
}
@@ -771,6 +775,10 @@ static double doubleUnaryOperation(Double D, int kind)
return operand;
case MINUS:
return -operand;
+ case INCR:
+ return operand + 1;
+ case DECR:
+ return operand - 1;
default:
throw new InterpreterError("bad double unaryOperation");
}
diff --git a/src/bsh/Reflect.java b/src/bsh/Reflect.java
index 9421024..301b626 100644
--- a/src/bsh/Reflect.java
+++ b/src/bsh/Reflect.java
@@ -37,6 +37,7 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
@@ -332,30 +333,27 @@ This method should be rewritten to use getFields() and avoid catching
*/
private static Field findAccessibleField(Class clas, String fieldName) throws UtilEvalError, NoSuchFieldException {
Field field;
-
// Quick check catches public fields include those in interfaces
try {
field = clas.getField(fieldName);
- field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
- // fallthrough
- }
-
- // Now, on with the hunt...
- while (clas != null) {
- try {
- field = clas.getDeclaredField(fieldName);
- field.setAccessible(true);
- return field;
-
- // Not found, fall through to next class
-
- } catch (NoSuchFieldException e) {
- // fallthrough
+ // ignore
+ }
+ if (Capabilities.haveAccessibility()) {
+ // try hidden fields (protected, private, package protected)
+ while (clas != null) {
+ try {
+ field = clas.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return field;
+ } catch (SecurityException e) {
+ break;
+ } catch (NoSuchFieldException e) {
+ // Not found, fall through to next class
+ }
+ clas = clas.getSuperclass();
}
-
- clas = clas.getSuperclass();
}
throw new NoSuchFieldException(fieldName);
}
@@ -435,8 +433,14 @@ protected static Method resolveJavaMethod(BshClassManager bcm, Class clas, Strin
// This is the first time we've seen this method, set accessibility
// Note: even if it's a public method, we may have found it in a
// non-public class
- if (method != null && (!publicOnly || isPublic(method))) {
- method.setAccessible(true);
+ if (method != null) {
+ if (!publicOnly || (isPublic(method) && !isPublic(method.getDeclaringClass()))) {
+ try {
+ method.setAccessible(true);
+ } catch (SecurityException e) {
+ method = null;
+ }
+ }
}
// If succeeded cache the resolved method.
@@ -565,7 +569,7 @@ static Object constructObject(Class clas, Object[] args) throws ReflectError, In
throw cantFindConstructor(clas, types);
}
- if (!isPublic(con)) {
+ if (!isPublic(con) && Capabilities.haveAccessibility()) {
con.setAccessible(true);
}
@@ -882,23 +886,26 @@ private static ReflectError cantFindConstructor(Class clas, Class[] types) {
}
- private static boolean isPublic(Class c) {
- return Modifier.isPublic(c.getModifiers());
+ private static boolean isPublic(Member member) {
+ return Modifier.isPublic(member.getModifiers());
}
- private static boolean isPublic(Method m) {
- return Modifier.isPublic(m.getModifiers());
+ private static boolean isPublic(Class clazz) {
+ return Modifier.isPublic(clazz.getModifiers());
}
- private static boolean isPublic(Constructor c) {
- return Modifier.isPublic(c.getModifiers());
+ private static boolean isStatic(Method m) {
+ return Modifier.isStatic(m.getModifiers());
}
- private static boolean isStatic(Method m) {
- return Modifier.isStatic(m.getModifiers());
+ static void setAccessible(final Field field) {
+ if ( ! isPublic(field) && Capabilities.haveAccessibility()) {
+ field.setAccessible(true);
+ }
}
+
}
diff --git a/src/bsh/Types.java b/src/bsh/Types.java
index fcce754..c4903fb 100644
--- a/src/bsh/Types.java
+++ b/src/bsh/Types.java
@@ -170,7 +170,7 @@ Is the assignment legal via original Java (up to version 1.4)
assignment rules, not including auto-boxing/unboxing.
@param rhsType may be null to indicate primitive null value
*/
- static boolean isJavaBaseAssignable( Class lhsType, Class rhsType )
+ static boolean isJavaBaseAssignable( Class> lhsType, Class> rhsType )
{
/*
Assignment to loose type, defer to bsh extensions
@@ -373,7 +373,7 @@ If fromValue is (or would be) Primitive.NULL then fromType should be null.
conversions... Where does that need to go?
*/
private static Object castObject(
- Class toType, Class fromType, Object fromValue,
+ Class> toType, Class> fromType, Object fromValue,
int operation, boolean checkOnly )
throws UtilEvalError
{
diff --git a/src/bsh/bsh.jjt b/src/bsh/bsh.jjt
index 036c5fb..54e77cd 100644
--- a/src/bsh/bsh.jjt
+++ b/src/bsh/bsh.jjt
@@ -360,6 +360,7 @@ TOKEN : /* LITERALS */
( ["n","t","b","r","f","\\","'","\""]
| ["0"-"7"] ( ["0"-"7"] )?
| ["0"-"3"] ["0"-"7"] ["0"-"7"]
+ | ["u"] (["0"-"9","a"-"f","A"-"F"])+
)
)
)
@@ -373,6 +374,7 @@ TOKEN : /* LITERALS */
( ["n","t","b","r","f","\\","'","\""]
| ["0"-"7"] ( ["0"-"7"] )?
| ["0"-"3"] ["0"-"7"] ["0"-"7"]
+ | ["u"] (["0"-"9","a"-"f","A"-"F"])+
)
)
)*
diff --git a/src/bsh/classpath/ClassManagerImpl.java b/src/bsh/classpath/ClassManagerImpl.java
index bef34ef..1d278b4 100644
--- a/src/bsh/classpath/ClassManagerImpl.java
+++ b/src/bsh/classpath/ClassManagerImpl.java
@@ -245,6 +245,10 @@ public Class classForName( String name )
*/
}
+ // Try scripted class
+ if ( c == null )
+ c = loadSourceClass( name );
+
// Cache result (or null for not found)
cacheClassInfo( name, c );
diff --git a/src/bsh/commands/print.bsh b/src/bsh/commands/print.bsh
index 846e366..15bee36 100644
--- a/src/bsh/commands/print.bsh
+++ b/src/bsh/commands/print.bsh
@@ -32,7 +32,7 @@ void print( arg )
print("}");
}
else
- this.interpreter.println(String.valueOf(arg));
+ this.interpreter.println(java.lang.String.valueOf(arg));
/*
Do we want to iterate over iterable things?
diff --git a/src/bsh/servlet/SimpleTemplate.java b/src/bsh/servlet/SimpleTemplate.java
index bc3a16f..dc6feaa 100644
--- a/src/bsh/servlet/SimpleTemplate.java
+++ b/src/bsh/servlet/SimpleTemplate.java
@@ -37,7 +37,7 @@ public class SimpleTemplate
{
StringBuffer buff;
static String NO_TEMPLATE = "NO_TEMPLATE"; // Flag for non-existent
- static Map templateData = new HashMap();
+ static Map templateData = new HashMap();
static boolean cacheTemplates = true;
/**
@@ -52,7 +52,7 @@ public class SimpleTemplate
*/
public static SimpleTemplate getTemplate( String file )
{
- String templateText = (String)templateData.get( file );
+ String templateText = templateData.get( file );
if ( templateText == null || !cacheTemplates ) {
try {
diff --git a/tests/junitTests/src/bsh/AnnotationsParsing.java b/tests/junitTests/src/bsh/AnnotationsParsing.java
new file mode 100644
index 0000000..6dc9d94
--- /dev/null
+++ b/tests/junitTests/src/bsh/AnnotationsParsing.java
@@ -0,0 +1,24 @@
+package bsh;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+
+/**
+ * See issue 24.
+ */
+@RunWith(FilteredTestRunner.class)
+public class AnnotationsParsing {
+
+ @Test
+ @Category(KnownIssue.class)
+ public void annotation_on_method_declaration() throws Exception {
+ TestUtil.eval(
+ "public int myMethod(final int i) {",
+ " return i * 7;",
+ "}",
+ "return myMethod(6);"
+ );
+ }
+}
diff --git a/tests/junitTests/src/bsh/GoogleReports.java b/tests/junitTests/src/bsh/GoogleReports.java
new file mode 100644
index 0000000..e010c3c
--- /dev/null
+++ b/tests/junitTests/src/bsh/GoogleReports.java
@@ -0,0 +1,85 @@
+package bsh;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+
+import static bsh.TestUtil.eval;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(FilteredTestRunner.class)
+public class GoogleReports {
+
+ /**
+ * issue#57
+ */
+ @Test
+ @SuppressWarnings({"ConstantIfStatement"})
+ public void issue_57() throws Exception {
+ int loopCount = 0;
+ do {
+ loopCount++;
+ if (true) continue;
+ } while (false);
+ assertEquals(1, loopCount);
+ loopCount = (Integer) eval(
+ "int loopCount = 0;",
+ "do{",
+ " loopCount++;",
+ " if (loopCount > 100) return loopCount;",
+ " if (true) continue;",
+ "} while (false);",
+ "return loopCount"
+ );
+ assertEquals(1, loopCount);
+ loopCount = (Integer) eval(
+ "int loopCount = 0;",
+ "while (loopCount < 1) {",
+ " loopCount++;",
+ " if (loopCount > 100) return loopCount;",
+ " if (true) continue;",
+ "}",
+ "return loopCount"
+ );
+ assertEquals(1, loopCount);
+ assertEquals(Boolean.TRUE, eval("while(true) { break; return false; } return true;"));
+ assertEquals(Boolean.TRUE, eval("do { break; return false; } while(true); return true;"));
+ loopCount = (Integer) eval(
+ "int loopCount = 0;",
+ "while (++loopCount < 2);",
+ "return loopCount"
+ );
+ assertEquals(2, loopCount);
+ loopCount = (Integer) eval(
+ "int loopCount = 0;",
+ "do { } while (++loopCount < 2);",
+ "return loopCount"
+ );
+ assertEquals(2, loopCount);
+ }
+
+
+ /**
+ * issue#60
+ */
+ @Test
+ public void issue_60() throws Exception {
+ final String script =
+ "String foo = null;" +
+ "if (foo != null && foo.length() > 0) return \"not empty\";" +
+ "return \"empty\";";
+ final ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
+ scriptEngineManager.registerEngineName("beanshell", new BshScriptEngineFactory());
+ final ScriptEngine engine = scriptEngineManager.getEngineByName("beanshell");
+ assertNotNull(engine);
+ Object result;
+ result = engine.eval(script);
+ assertEquals("empty", result);
+ result = eval(script);
+ assertEquals("empty", result);
+ }
+
+}
diff --git a/tests/junitTests/src/bsh/Issue_55_Test.java b/tests/junitTests/src/bsh/Issue_55_Test.java
index 08902d9..d614c73 100644
--- a/tests/junitTests/src/bsh/Issue_55_Test.java
+++ b/tests/junitTests/src/bsh/Issue_55_Test.java
@@ -4,9 +4,12 @@
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
+import javax.script.ScriptException;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
@RunWith(FilteredTestRunner.class)
public class Issue_55_Test {
@@ -29,4 +32,16 @@ public void check_ExternalNameSpace() throws Exception {
assertNull("variable 'a' should have value ", externalNameSpace.getMap().get("a"));
}
+ @Category( NotSuitedFor_Java5_OrLower.class )
+ @Test
+ public void issue_67() throws Exception {
+ final String script = "print(\"test\";";
+ try {
+ new BshScriptEngineFactory().getScriptEngine().eval(script);
+ fail("expected script exception");
+ } catch (ScriptException e) {
+ assertEquals(1, e.getLineNumber());
+ }
+ }
+
}
diff --git a/tests/junitTests/src/bsh/StringLiteralTest.java b/tests/junitTests/src/bsh/StringLiteralTest.java
index 3a4d194..b63b298 100644
--- a/tests/junitTests/src/bsh/StringLiteralTest.java
+++ b/tests/junitTests/src/bsh/StringLiteralTest.java
@@ -72,6 +72,18 @@ public void parse_long_string_literal_multiline() throws Exception {
assertStringParsing("test\ntest", DelimiterMode.MULTI_LINE);
}
+ @Test
+ public void parse_unicode_literals_in_comment() throws Exception {
+ final Interpreter interpreter = new Interpreter();
+ Object result = interpreter.eval("// source path: C:\\user\\desktop");
+ Assert.assertEquals(result, null);
+ char c = (char) interpreter.eval("return '\\u51ea\'");
+ Assert.assertEquals(c, '\u51ea');
+ String s = (String) interpreter.eval("return \"\\u51EA1234\"; // \\user\\desktop");
+ System.out.println(s);
+ Assert.assertEquals(s, "\u51EA1234");
+ }
+
private void assertStringParsing(final String s, final DelimiterMode mode) throws EvalError {
assertStringParsing(s, s, mode);