diff --git a/pom.xml b/pom.xml
index 64e28e3a..9dff8c8e 100755
--- a/pom.xml
+++ b/pom.xml
@@ -11,14 +11,14 @@
aviator
5.4.2-SNAPSHOT
aviator
- A lightweight, high performance expression evaluator for java
+ A high performance scripting language hosted on the JVM
https://github.com/killme2008/aviator
2010
dennis zhuang
- http://fnil.net/
+ https://github.com/killme2008
8
@@ -51,7 +51,7 @@
org.springframework
spring-context
- 4.3.16.RELEASE
+ 5.3.36
provided
@@ -118,8 +118,8 @@
maven-compiler-plugin
3.8.1
-
- 1.7
+
+ 1.8
UTF-8
diff --git a/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java b/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java
index 2dddd77b..40fa7bcd 100644
--- a/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java
+++ b/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java
@@ -404,10 +404,11 @@ private Map loadScript0(final String abPath) throws IOException
@SuppressWarnings("unchecked")
private Map executeModule(final Expression exp, final String abPath) {
final Env exports = new Env();
+ exports.configure(this, exp, -1);
final Map module = exp.newEnv("exports", exports, "path", abPath);
Map env = exp.newEnv("__MODULE__", module, "exports", exports);
- exp.execute(env);
- exports.configure(this, exp);
+ ((BaseExpression) exp).execute(env, false);
+
return (Map) module.get("exports");
}
diff --git a/src/main/java/com/googlecode/aviator/BaseExpression.java b/src/main/java/com/googlecode/aviator/BaseExpression.java
index f664a948..fc5cba1b 100644
--- a/src/main/java/com/googlecode/aviator/BaseExpression.java
+++ b/src/main/java/com/googlecode/aviator/BaseExpression.java
@@ -24,10 +24,12 @@
import com.googlecode.aviator.parser.VariableMeta;
import com.googlecode.aviator.runtime.FunctionArgument;
import com.googlecode.aviator.runtime.LambdaFunctionBootstrap;
+import com.googlecode.aviator.runtime.RuntimeUtils;
import com.googlecode.aviator.runtime.function.LambdaFunction;
import com.googlecode.aviator.utils.Constants;
import com.googlecode.aviator.utils.Env;
import com.googlecode.aviator.utils.Reflector;
+import com.googlecode.aviator.utils.Utils;
/**
* Base expression
@@ -241,10 +243,14 @@ public Map newEnv(final Object... args) {
@Override
public Object execute(Map map) {
+ return this.execute(map, true);
+ }
+
+ protected Object execute(Map map, boolean checkExecutionTimeout) {
if (map == null) {
map = Collections.emptyMap();
}
- Env env = genTopEnv(map);
+ Env env = genTopEnv(map, checkExecutionTimeout);
EnvProcessor envProcessor = this.instance.getEnvProcessor();
if (envProcessor != null) {
envProcessor.beforeExecute(env, this);
@@ -327,23 +333,25 @@ public List getVariableNames() {
return this.varNames;
}
- protected Env newEnv(final Map map, final boolean direct) {
+ protected Env newEnv(final Map map, final boolean direct,
+ boolean checkExecutionTimeout) {
Env env;
if (direct) {
env = new Env(map, map == Collections.EMPTY_MAP ? new HashMap() : map);
} else {
env = new Env(map);
}
- env.configure(this.instance, this);
+ env.configure(this.instance, this, getExecutionStartNs(checkExecutionTimeout));
return env;
}
- protected Env genTopEnv(final Map map) {
+ protected Env genTopEnv(final Map map, boolean checkExecutionTimeout) {
if (map instanceof Env) {
- ((Env) map).configure(this.instance, this);
+ ((Env) map).configure(this.instance, this, getExecutionStartNs(checkExecutionTimeout));
}
Env env =
- newEnv(map, this.instance.getOptionValue(Options.USE_USER_ENV_AS_TOP_ENV_DIRECTLY).bool);
+ newEnv(map, this.instance.getOptionValue(Options.USE_USER_ENV_AS_TOP_ENV_DIRECTLY).bool,
+ checkExecutionTimeout);
if (this.compileEnv != null && !this.compileEnv.isEmpty()) {
env.putAll(this.compileEnv);
@@ -354,8 +362,16 @@ protected Env genTopEnv(final Map map) {
return env;
}
+ private long getExecutionStartNs(boolean checkExecutionTimeout) {
+ long startNs = -1;
+ if (checkExecutionTimeout && this.instance.getOptionValue(Options.EVAL_TIMEOUT_MS).number > 0) {
+ startNs = Utils.currentTimeNanos();
+ }
+ return startNs;
+ }
+
protected Env newEnv(final Map map) {
- return newEnv(map, false);
+ return newEnv(map, false, true);
}
public Map getLambdaBootstraps() {
diff --git a/src/main/java/com/googlecode/aviator/Options.java b/src/main/java/com/googlecode/aviator/Options.java
index 3e81201c..b1111d88 100644
--- a/src/main/java/com/googlecode/aviator/Options.java
+++ b/src/main/java/com/googlecode/aviator/Options.java
@@ -2,6 +2,7 @@
import java.math.MathContext;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import com.googlecode.aviator.utils.Utils;
@@ -117,9 +118,30 @@ public enum Options {
/**
* Whether the compiled expression is serializable. If true, the compiled expression will
- * implement {@link #jva.io.Serializable} and can be encoded/decoded by java serialization.
+ * implement {@link jva.io.Serializable} and can be encoded/decoded by java serialization.
*/
- SERIALIZABLE;
+ SERIALIZABLE,
+
+ /**
+ *
+ * The expression execution timeout value in milliseconds. If the execution time exceeds this
+ * value, it will throw a {@link com.googlecode.aviator.exception.TimeoutException}. A value of
+ * zero or less indicates no timeout limitation, the default value is zero (no limitation).
+ *
+ * Note: this limitation is not strict and may hurt performance, it is only checked before:
+ *
+ * - Operator evaluating, such as add, sub etc.
+ * - Jumping in branches, such as loop and conditional clauses etc.
+ * - Function invocation
+ *
+ *
+ * So if the expression doesn't contains these clauses or trapped into a function invocation, the
+ * behavior may be not expected. Try its best, but no promises.
+ *
+ * @since 5.4.2
+ *
+ */
+ EVAL_TIMEOUT_MS;
/**
@@ -135,6 +157,8 @@ public static class Value {
public Set featureSet;
public Set> classes;
public EvalMode evalMode;
+ // Temporal cached number value to avoid expensive calculation.
+ public long cachedNumber;
public Value(final EvalMode evalMode) {
super();
@@ -200,6 +224,7 @@ public Object intoObject(final Value val) {
return val.bool;
case MAX_LOOP_COUNT:
case OPTIMIZE_LEVEL:
+ case EVAL_TIMEOUT_MS:
return val.number;
case FEATURE_SET:
return val.featureSet;
@@ -243,6 +268,14 @@ public Value intoValue(final Object val) {
return COMPILE_VALUE;
}
}
+ case EVAL_TIMEOUT_MS: {
+ Value value = new Value(((Number) val).intValue());
+ // Cached the converted result.
+ if (value.number > 0) {
+ value.cachedNumber = TimeUnit.NANOSECONDS.convert(value.number, TimeUnit.MILLISECONDS);
+ }
+ return value;
+ }
case MAX_LOOP_COUNT:
return new Value(((Number) val).intValue());
case ALLOWED_CLASS_SET:
@@ -278,6 +311,7 @@ public boolean isValidValue(final Object val) {
final int level = ((Integer) val).intValue();
return val instanceof Integer
&& (level == AviatorEvaluator.EVAL || level == AviatorEvaluator.COMPILE);
+ case EVAL_TIMEOUT_MS:
case MAX_LOOP_COUNT:
return val instanceof Long || val instanceof Integer;
case MATH_CONTEXT:
@@ -356,6 +390,8 @@ public Value getDefaultValueObject() {
return NULL_CLASS_SET;
case EVAL_MODE:
return getDefaultEvalMode();
+ case EVAL_TIMEOUT_MS:
+ return ZERO_VALUE;
}
return null;
}
diff --git a/src/main/java/com/googlecode/aviator/code/OptimizeCodeGenerator.java b/src/main/java/com/googlecode/aviator/code/OptimizeCodeGenerator.java
index 0060fd89..b8c93ffb 100644
--- a/src/main/java/com/googlecode/aviator/code/OptimizeCodeGenerator.java
+++ b/src/main/java/com/googlecode/aviator/code/OptimizeCodeGenerator.java
@@ -373,6 +373,10 @@ public Expression getResult(final boolean unboxObject) {
if (SymbolTable.isReservedKeyword((Variable) token)) {
continue;
}
+ // Ignore class name or package name
+ if (token.getMeta(Constants.USE_CLASS_PKG, false)) {
+ continue;
+ }
String varName = token.getLexeme();
VariableMeta meta = variables.get(varName);
diff --git a/src/main/java/com/googlecode/aviator/code/asm/ASMCodeGenerator.java b/src/main/java/com/googlecode/aviator/code/asm/ASMCodeGenerator.java
index b90a8c26..3af29665 100644
--- a/src/main/java/com/googlecode/aviator/code/asm/ASMCodeGenerator.java
+++ b/src/main/java/com/googlecode/aviator/code/asm/ASMCodeGenerator.java
@@ -453,6 +453,7 @@ private void visitRightBranch(final Token> lookhead, final int ints,
public void onTernaryBoolean(final Token> lookhead) {
loadEnv();
visitLineNumber(lookhead);
+ checkExecutionTimeout();
visitBoolean();
Label l0 = makeLabel();
Label l1 = makeLabel();
@@ -472,6 +473,7 @@ private void pushLabel1(final Label l1) {
@Override
public void onTernaryLeft(final Token> lookhead) {
+ checkExecutionTimeout();
this.mv.visitJumpInsn(GOTO, peekLabel1());
visitLabel(popLabel0());
visitLineNumber(lookhead);
@@ -484,6 +486,7 @@ private Label peekLabel1() {
@Override
public void onTernaryRight(final Token> lookhead) {
+ checkExecutionTimeout();
visitLabel(popLabel1());
visitLineNumber(lookhead);
this.popOperand(); // pop one boolean
@@ -570,6 +573,7 @@ public void onNeq(final Token> lookhead) {
private void doCompareAndJump(final Token> lookhead, final int ints,
final OperatorType opType) {
visitLineNumber(lookhead);
+ this.checkExecutionTimeout();
loadEnv();
visitCompare(ints, opType);
this.popOperand();
@@ -641,6 +645,7 @@ public void onNot(final Token> lookhead) {
private void visitBinOperator(final Token> token, final OperatorType opType,
final String methodName) {
visitLineNumber(token);
+ this.checkExecutionTimeout();
if (!OperationRuntime.hasRuntimeContext(this.compileEnv, opType)) {
// swap arguments for regular-expression match operator.
if (opType == OperatorType.MATCH) {
@@ -1288,6 +1293,9 @@ public void genNewLambdaCode(final LambdaFunctionBootstrap bootstrap) {
@Override
public void onMethodName(final Token> lookhead) {
+
+ checkExecutionTimeout();
+
String outtterMethodName = "lambda";
if (lookhead.getType() != TokenType.Delegate) {
outtterMethodName = lookhead.getLexeme();
@@ -1318,6 +1326,13 @@ public void onMethodName(final Token> lookhead) {
this.methodMetaDataStack.push(new MethodMetaData(lookhead, outtterMethodName));
}
+ private void checkExecutionTimeout() {
+ loadEnv();
+ this.mv.visitMethodInsn(INVOKESTATIC, RUNTIME_UTILS, "checkExecutionTimedOut",
+ "(Ljava/util/Map;)V");
+ this.popOperand();
+ }
+
private void loadAviatorFunction(final String outterMethodName, final String innerMethodName) {
Map name2Index = this.labelNameIndexMap.get(this.currentLabel);
// Is it stored in local?
diff --git a/src/main/java/com/googlecode/aviator/code/interpreter/IR.java b/src/main/java/com/googlecode/aviator/code/interpreter/IR.java
index 82585b25..67cc5f4c 100644
--- a/src/main/java/com/googlecode/aviator/code/interpreter/IR.java
+++ b/src/main/java/com/googlecode/aviator/code/interpreter/IR.java
@@ -10,4 +10,13 @@
*/
public interface IR extends Serializable {
void eval(InterpretContext context);
+
+ /**
+ * Returns true when the IR execution cost may be expensive
+ *
+ * @return
+ */
+ default boolean mayBeCost() {
+ return false;
+ }
}
diff --git a/src/main/java/com/googlecode/aviator/code/interpreter/InterpretContext.java b/src/main/java/com/googlecode/aviator/code/interpreter/InterpretContext.java
index 040cc2eb..af295de0 100644
--- a/src/main/java/com/googlecode/aviator/code/interpreter/InterpretContext.java
+++ b/src/main/java/com/googlecode/aviator/code/interpreter/InterpretContext.java
@@ -2,7 +2,9 @@
import java.util.ArrayDeque;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import com.googlecode.aviator.InterpretExpression;
+import com.googlecode.aviator.exception.TimeoutException;
import com.googlecode.aviator.lexer.token.Token;
import com.googlecode.aviator.parser.VariableMeta;
import com.googlecode.aviator.runtime.RuntimeUtils;
@@ -138,6 +140,9 @@ public void dispatch(final boolean next) {
}
if (this.pc != null) {
+ if (this.pc.mayBeCost()) {
+ RuntimeUtils.checkExecutionTimedOut(env);
+ }
if (this.trace) {
RuntimeUtils.printlnTrace(this.env, " " + this.pc + " " + descOperandsStack());
}
diff --git a/src/main/java/com/googlecode/aviator/code/interpreter/ir/BranchIfIR.java b/src/main/java/com/googlecode/aviator/code/interpreter/ir/BranchIfIR.java
index 52a0c5e9..702ada30 100644
--- a/src/main/java/com/googlecode/aviator/code/interpreter/ir/BranchIfIR.java
+++ b/src/main/java/com/googlecode/aviator/code/interpreter/ir/BranchIfIR.java
@@ -41,6 +41,13 @@ public void eval(final InterpretContext context) {
}
}
+
+
+ @Override
+ public boolean mayBeCost() {
+ return true;
+ }
+
@Override
public String toString() {
return "branch_if " + this.pc + " [" + this.label + "] " + this.sourceInfo;
diff --git a/src/main/java/com/googlecode/aviator/code/interpreter/ir/BranchUnlessIR.java b/src/main/java/com/googlecode/aviator/code/interpreter/ir/BranchUnlessIR.java
index 05398384..cd690572 100644
--- a/src/main/java/com/googlecode/aviator/code/interpreter/ir/BranchUnlessIR.java
+++ b/src/main/java/com/googlecode/aviator/code/interpreter/ir/BranchUnlessIR.java
@@ -42,6 +42,11 @@ public void eval(final InterpretContext context) {
}
}
+ @Override
+ public boolean mayBeCost() {
+ return true;
+ }
+
@Override
public String toString() {
return "branch_unless " + this.pc + " [" + this.label + "] " + this.sourceInfo;
diff --git a/src/main/java/com/googlecode/aviator/code/interpreter/ir/GotoIR.java b/src/main/java/com/googlecode/aviator/code/interpreter/ir/GotoIR.java
index 6663ff8c..49a9b388 100644
--- a/src/main/java/com/googlecode/aviator/code/interpreter/ir/GotoIR.java
+++ b/src/main/java/com/googlecode/aviator/code/interpreter/ir/GotoIR.java
@@ -30,6 +30,11 @@ public Label getLabel() {
return this.label;
}
+ @Override
+ public boolean mayBeCost() {
+ return true;
+ }
+
@Override
public void eval(final InterpretContext context) {
context.jumpTo(this.pc);
diff --git a/src/main/java/com/googlecode/aviator/code/interpreter/ir/OperatorIR.java b/src/main/java/com/googlecode/aviator/code/interpreter/ir/OperatorIR.java
index bf483841..65f445fe 100644
--- a/src/main/java/com/googlecode/aviator/code/interpreter/ir/OperatorIR.java
+++ b/src/main/java/com/googlecode/aviator/code/interpreter/ir/OperatorIR.java
@@ -110,7 +110,10 @@ public void eval(final InterpretContext context) {
context.dispatch();
}
-
+ @Override
+ public boolean mayBeCost() {
+ return true;
+ }
public OperatorType getOp() {
return this.op;
diff --git a/src/main/java/com/googlecode/aviator/code/interpreter/ir/SendIR.java b/src/main/java/com/googlecode/aviator/code/interpreter/ir/SendIR.java
index 78496c4f..04d9ce3b 100644
--- a/src/main/java/com/googlecode/aviator/code/interpreter/ir/SendIR.java
+++ b/src/main/java/com/googlecode/aviator/code/interpreter/ir/SendIR.java
@@ -138,6 +138,11 @@ public void eval(final InterpretContext context) {
context.dispatch();
}
+ @Override
+ public boolean mayBeCost() {
+ return true;
+ }
+
@Override
public String toString() {
return "send " + (this.name == null ? "" : this.name) + ", " + this.arity + ", "
diff --git a/src/main/java/com/googlecode/aviator/exception/TimeoutException.java b/src/main/java/com/googlecode/aviator/exception/TimeoutException.java
new file mode 100644
index 00000000..7cdebbc0
--- /dev/null
+++ b/src/main/java/com/googlecode/aviator/exception/TimeoutException.java
@@ -0,0 +1,29 @@
+package com.googlecode.aviator.exception;
+
+/**
+ * The expression execution is timed out.
+ *
+ * @author dennis(killme2008@gmail.com)
+ * @since 5.4.2
+ */
+public class TimeoutException extends ExpressionRuntimeException {
+
+ private static final long serialVersionUID = -3749680912179160158L;
+
+ public TimeoutException() {
+ super();
+ }
+
+ public TimeoutException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ public TimeoutException(final String message) {
+ super(message);
+ }
+
+ public TimeoutException(final Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/src/main/java/com/googlecode/aviator/parser/ExpressionParser.java b/src/main/java/com/googlecode/aviator/parser/ExpressionParser.java
index 62b37d8e..fa6e28dd 100644
--- a/src/main/java/com/googlecode/aviator/parser/ExpressionParser.java
+++ b/src/main/java/com/googlecode/aviator/parser/ExpressionParser.java
@@ -1603,7 +1603,7 @@ private void className() {
wildcard();
} else {
checkVariableName(this.lookhead);
- getCodeGenerator().onConstant(this.lookhead);
+ getCodeGenerator().onConstant(this.lookhead.withMeta(Constants.USE_CLASS_PKG, true));
}
move(true);
}
diff --git a/src/main/java/com/googlecode/aviator/runtime/RuntimeUtils.java b/src/main/java/com/googlecode/aviator/runtime/RuntimeUtils.java
index f2895c9a..18f64415 100644
--- a/src/main/java/com/googlecode/aviator/runtime/RuntimeUtils.java
+++ b/src/main/java/com/googlecode/aviator/runtime/RuntimeUtils.java
@@ -3,9 +3,11 @@
import java.io.IOException;
import java.math.MathContext;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import com.googlecode.aviator.Options;
+import com.googlecode.aviator.exception.TimeoutException;
import com.googlecode.aviator.runtime.function.LambdaFunction;
import com.googlecode.aviator.runtime.function.internal.UnpackingArgsFunction;
import com.googlecode.aviator.runtime.type.AviatorFunction;
@@ -19,6 +21,7 @@
import com.googlecode.aviator.runtime.type.seq.LimitedSequence;
import com.googlecode.aviator.runtime.type.seq.MapSequence;
import com.googlecode.aviator.utils.Env;
+import com.googlecode.aviator.utils.Utils;
/**
* Runtime utils
@@ -101,6 +104,22 @@ public static Sequence seq(final Object o, final Map env) {
return seq;
}
+ public static void checkExecutionTimedOut(final Map env) {
+ if (env instanceof Env) {
+ long startNs = ((Env) env).getStartNs();
+ if (startNs > 0) {
+ long execTimeoutNs = getEvalTimeoutNs(env);
+ if (execTimeoutNs > 0) {
+ if (Utils.currentTimeNanos() - startNs > execTimeoutNs) {
+ throw new TimeoutException("Expression execution timed out, exceeded: "
+ + getInstance(env).getOptionValue(Options.EVAL_TIMEOUT_MS).number + " ms");
+ }
+ }
+ }
+ }
+
+ }
+
/**
* Ensure the object is not null, cast null into AviatorNil.
*
@@ -131,6 +150,11 @@ public static final boolean isTracedEval(final Map env) {
return getInstance(env).getOptionValue(Options.TRACE_EVAL).bool;
}
+ // Returns the eval timeout value in nanoseconds
+ public static final long getEvalTimeoutNs(final Map env) {
+ return getInstance(env).getOptionValue(Options.EVAL_TIMEOUT_MS).cachedNumber;
+ }
+
public static AviatorFunction getFunction(final Object object, final Map env) {
if (object instanceof AviatorFunction) {
return (AviatorFunction) object;
diff --git a/src/main/java/com/googlecode/aviator/runtime/function/LambdaFunction.java b/src/main/java/com/googlecode/aviator/runtime/function/LambdaFunction.java
index 85136b70..7e1ddcf2 100644
--- a/src/main/java/com/googlecode/aviator/runtime/function/LambdaFunction.java
+++ b/src/main/java/com/googlecode/aviator/runtime/function/LambdaFunction.java
@@ -241,9 +241,9 @@ protected Map newEnv(final Map parentEnv,
Env env = null;
if (!this.inheritEnv) {
final Env contextEnv = new Env(parentEnv, this.context);
- contextEnv.configure(this.context.getInstance(), this.expression);
+ contextEnv.configure(this.context.getInstance(), this.expression, this.context.getStartNs());
env = new Env(contextEnv);
- env.configure(this.context.getInstance(), this.expression);
+ env.configure(this.context.getInstance(), this.expression, this.context.getStartNs());
} else {
// assert (parentEnv == this.context);
env = (Env) parentEnv;
diff --git a/src/main/java/com/googlecode/aviator/runtime/function/system/BigIntFunction.java b/src/main/java/com/googlecode/aviator/runtime/function/system/BigIntFunction.java
index e3b5b015..43112c2a 100644
--- a/src/main/java/com/googlecode/aviator/runtime/function/system/BigIntFunction.java
+++ b/src/main/java/com/googlecode/aviator/runtime/function/system/BigIntFunction.java
@@ -36,7 +36,9 @@ public AviatorObject call(final Map env, final AviatorObject arg
} else if (obj instanceof Character) {
return AviatorBigInt.valueOf(new BigInteger(String.valueOf(obj)));
} else {
- throw new ClassCastException("Could not cast " + (obj != null ? obj.getClass().getName() : "null") + " to bigint, AviatorObject is " + arg1);
+ throw new ClassCastException(
+ "Could not cast " + (obj != null ? obj.getClass().getName() : "null")
+ + " to bigint, AviatorObject is " + arg1);
}
case String:
return AviatorBigInt.valueOf(new BigInteger((String) arg1.getValue(env)));
diff --git a/src/main/java/com/googlecode/aviator/runtime/function/system/DoubleFunction.java b/src/main/java/com/googlecode/aviator/runtime/function/system/DoubleFunction.java
index 0a44f73c..0e592a7e 100644
--- a/src/main/java/com/googlecode/aviator/runtime/function/system/DoubleFunction.java
+++ b/src/main/java/com/googlecode/aviator/runtime/function/system/DoubleFunction.java
@@ -34,7 +34,9 @@ public AviatorObject call(Map env, AviatorObject arg1) {
} else if (obj instanceof Character) {
return new AviatorDouble(Double.parseDouble(String.valueOf(obj)));
} else {
- throw new ClassCastException("Could not cast " + (obj != null ? obj.getClass().getName() : "null") + " to double, AviatorObject is" + arg1 );
+ throw new ClassCastException(
+ "Could not cast " + (obj != null ? obj.getClass().getName() : "null")
+ + " to double, AviatorObject is" + arg1);
}
case String:
return new AviatorDouble(Double.parseDouble((String) arg1.getValue(env)));
diff --git a/src/main/java/com/googlecode/aviator/runtime/function/system/LongFunction.java b/src/main/java/com/googlecode/aviator/runtime/function/system/LongFunction.java
index 07ff6fd6..a9425ff0 100644
--- a/src/main/java/com/googlecode/aviator/runtime/function/system/LongFunction.java
+++ b/src/main/java/com/googlecode/aviator/runtime/function/system/LongFunction.java
@@ -34,7 +34,9 @@ public AviatorObject call(Map env, AviatorObject arg1) {
} else if (obj instanceof Character) {
return AviatorLong.valueOf(Long.valueOf(String.valueOf(obj)));
} else {
- throw new ClassCastException("Could not cast " + (obj != null ? obj.getClass().getName() : "null") + " to long, AviatorObject is " + arg1);
+ throw new ClassCastException(
+ "Could not cast " + (obj != null ? obj.getClass().getName() : "null")
+ + " to long, AviatorObject is " + arg1);
}
case String:
return AviatorLong.valueOf(Long.valueOf((String) arg1.getValue(env)));
diff --git a/src/main/java/com/googlecode/aviator/runtime/op/OperationRuntime.java b/src/main/java/com/googlecode/aviator/runtime/op/OperationRuntime.java
index f8828455..cf4ce6d7 100644
--- a/src/main/java/com/googlecode/aviator/runtime/op/OperationRuntime.java
+++ b/src/main/java/com/googlecode/aviator/runtime/op/OperationRuntime.java
@@ -128,7 +128,6 @@ public static AviatorObject eval(final AviatorObject left, final Map env, final OperatorType opType) {
-
AviatorFunction func = RuntimeUtils.getInstance(env).getOpFunction(opType);
AviatorObject ret = eval0(left, right, env, opType, func);
if (RuntimeUtils.isTracedEval(env)) {
diff --git a/src/main/java/com/googlecode/aviator/utils/Constants.java b/src/main/java/com/googlecode/aviator/utils/Constants.java
index 6b55f67b..5e7f00fe 100644
--- a/src/main/java/com/googlecode/aviator/utils/Constants.java
+++ b/src/main/java/com/googlecode/aviator/utils/Constants.java
@@ -44,6 +44,7 @@ public class Constants {
// Whether string has interpolation point.
public static final String INTER_META = "hasInterpolation";
public static final String UNPACK_ARGS = "unpackingArgs";
+ public static final String USE_CLASS_PKG = "useClassOrPkg";
public static final Pattern SPLIT_PAT = Pattern.compile("\\.");
// runtime metadata keys
diff --git a/src/main/java/com/googlecode/aviator/utils/Env.java b/src/main/java/com/googlecode/aviator/utils/Env.java
index d21342d0..3a551fcb 100644
--- a/src/main/java/com/googlecode/aviator/utils/Env.java
+++ b/src/main/java/com/googlecode/aviator/utils/Env.java
@@ -33,7 +33,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.Callable;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.Feature;
@@ -75,6 +74,10 @@ public class Env implements Map, Serializable {
public static final Map EMPTY_ENV = Collections.emptyMap();
+ // The execution start timestamp in nanoseconds.
+ private transient long startNs = -1;
+
+
/**
* Constructs an env instance with empty state.
*/
@@ -100,6 +103,10 @@ public void setmOverrides(final Map mOverrides) {
this.mOverrides = mOverrides;
}
+ public long getStartNs() {
+ return startNs;
+ }
+
public List getImportedSymbols() {
return this.importedSymbols;
}
@@ -148,9 +155,18 @@ public void setInstance(final AviatorEvaluatorInstance instance) {
this.instance = instance;
}
- public void configure(final AviatorEvaluatorInstance instance, final Expression exp) {
+ // Configure the env.
+ public void configure(final AviatorEvaluatorInstance instance, final Expression exp,
+ long startNs) {
this.instance = instance;
this.expression = exp;
+ setStartNs(startNs);
+ }
+
+ private void setStartNs(long startNs) {
+ if (this.startNs == -1 && startNs > 0) {
+ this.startNs = startNs;
+ }
}
private String findSymbol(final String name) throws ClassNotFoundException {
diff --git a/src/main/java/com/googlecode/aviator/utils/Utils.java b/src/main/java/com/googlecode/aviator/utils/Utils.java
index a714a1d4..97ceef1e 100644
--- a/src/main/java/com/googlecode/aviator/utils/Utils.java
+++ b/src/main/java/com/googlecode/aviator/utils/Utils.java
@@ -29,6 +29,10 @@ private Utils() {
}
+ public static long currentTimeNanos() {
+ return System.nanoTime();
+ }
+
private static final ThreadLocal MESSAGE_DIGEST_LOCAL =
new ThreadLocal() {
diff --git a/src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceCompatibleUnitTest.java b/src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceCompatibleUnitTest.java
index 7c310a77..2512e710 100644
--- a/src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceCompatibleUnitTest.java
+++ b/src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceCompatibleUnitTest.java
@@ -23,12 +23,22 @@ public void testMaxLoopCount() {
super.testMaxLoopCount();
}
+ @Test
+ public void testEvalTimeout() {
+ // ignore
+ }
+
@Override
@Test
public void testIssue476() {
// ignore
}
+ @Test
+ public void testEvalTimeoutAndTryAgain() throws Exception {
+ // ignore
+ }
+
@Override
@Test
public void testClassAllowList() {
diff --git a/src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceInterpreteUnitTest.java b/src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceInterpreteUnitTest.java
new file mode 100644
index 00000000..53eae37b
--- /dev/null
+++ b/src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceInterpreteUnitTest.java
@@ -0,0 +1,20 @@
+package com.googlecode.aviator;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class AviatorEvaluatorInstanceInterpreteUnitTest extends AviatorEvaluatorInstanceUnitTest {
+
+
+ @Override
+ @Before
+ public void setup() {
+ super.setup();
+ this.instance.setOption(Options.EVAL_MODE, EvalMode.INTERPRETER);
+ }
+
+ @Test
+ public void testTraceEval() throws Exception {
+ // ignore
+ }
+}
diff --git a/src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceUnitTest.java b/src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceUnitTest.java
index baae2dc9..d656c61d 100644
--- a/src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceUnitTest.java
+++ b/src/test/java/com/googlecode/aviator/AviatorEvaluatorInstanceUnitTest.java
@@ -38,6 +38,7 @@
import com.googlecode.aviator.exception.CompileExpressionErrorException;
import com.googlecode.aviator.exception.ExpressionRuntimeException;
import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
+import com.googlecode.aviator.exception.TimeoutException;
import com.googlecode.aviator.exception.UnsupportedFeatureException;
import com.googlecode.aviator.lexer.token.OperatorType;
import com.googlecode.aviator.runtime.function.AbstractFunction;
@@ -57,6 +58,7 @@ public class AviatorEvaluatorInstanceUnitTest {
@Before
public void setup() {
this.instance = AviatorEvaluator.newInstance();
+ this.instance.setOption(Options.EVAL_TIMEOUT_MS, 100);
}
@Test
@@ -67,6 +69,26 @@ public void testIssue476() {
assertEquals(expr.getVariableFullNames(), Arrays.asList("x"));
}
+ @Test(expected = TimeoutException.class)
+ public void testEvalTimeout() {
+ this.instance.execute("while(true) { }");
+ }
+
+ @Test
+ public void testEvalTimeoutAndTryAgain() throws Exception {
+ Expression exp = this.instance.compile("Thread.sleep(120); a + 1");
+ try {
+ exp.execute(exp.newEnv("a", 2));
+ } catch (TimeoutException e) {
+ assertTrue(e.getMessage().contains("Expression execution timed out, exceeded: 100 ms"));
+ // ignore
+ }
+ this.instance.setOption(Options.EVAL_TIMEOUT_MS, 200);
+ assertEquals(exp.execute(exp.newEnv("a", 2)), 3);
+ Thread.sleep(500);
+ assertEquals(exp.execute(exp.newEnv("a", 2)), 3);
+ }
+
@SuppressWarnings("unchecked")
@Test
public void testIssue466() {
@@ -680,7 +702,6 @@ public void testTraceEval() throws Exception {
this.instance.setOption(Options.TRACE_EVAL, false);
this.instance.setTraceOutputStream(System.out);
}
-
}
@Test(expected = CompileExpressionErrorException.class)
diff --git a/src/test/java/com/googlecode/aviator/AviatorEvaluatorUnitTest.java b/src/test/java/com/googlecode/aviator/AviatorEvaluatorUnitTest.java
index 31d35b28..ce673dc6 100644
--- a/src/test/java/com/googlecode/aviator/AviatorEvaluatorUnitTest.java
+++ b/src/test/java/com/googlecode/aviator/AviatorEvaluatorUnitTest.java
@@ -42,6 +42,13 @@ public void testCompileWithoutCache() {
assertEquals(4, exp2.execute(null));
}
+ @Test
+ public void testIssue597() {
+ String s = "use java.lang.Thread;\n" + "Thread.sleep(2000);\n" + "return 1 > 0;";
+
+ Expression exp = AviatorEvaluator.compile(s);
+ assertTrue(exp.getVariableNames().isEmpty());
+ }
@Test
public void testNewEnv() {
diff --git a/src/test/java/com/googlecode/aviator/runtime/function/system/BigIntFunctionUnitTest.java b/src/test/java/com/googlecode/aviator/runtime/function/system/BigIntFunctionUnitTest.java
index ecf2e382..bab7b7d2 100644
--- a/src/test/java/com/googlecode/aviator/runtime/function/system/BigIntFunctionUnitTest.java
+++ b/src/test/java/com/googlecode/aviator/runtime/function/system/BigIntFunctionUnitTest.java
@@ -2,26 +2,25 @@
import com.googlecode.aviator.runtime.type.AviatorJavaType;
import org.junit.Test;
-
import java.util.HashMap;
import java.util.Map;
public class BigIntFunctionUnitTest {
- @Test(expected = ClassCastException.class)
- public void testCall_WithJavaTypeNullArgument() {
- BigIntFunction bigIntFunction = new BigIntFunction();
- AviatorJavaType aviatorJavaType = new AviatorJavaType("var");
- bigIntFunction.call(null, aviatorJavaType);
- }
+ @Test(expected = ClassCastException.class)
+ public void testCall_WithJavaTypeNullArgument() {
+ BigIntFunction bigIntFunction = new BigIntFunction();
+ AviatorJavaType aviatorJavaType = new AviatorJavaType("var");
+ bigIntFunction.call(null, aviatorJavaType);
+ }
- @Test(expected = ClassCastException.class)
- public void testCall_WithJavaTypeNotSupportArgument() {
- BigIntFunction bigIntFunction = new BigIntFunction();
- AviatorJavaType aviatorJavaType = new AviatorJavaType("var");
- Map env = new HashMap<>();
- env.put("var", true);
+ @Test(expected = ClassCastException.class)
+ public void testCall_WithJavaTypeNotSupportArgument() {
+ BigIntFunction bigIntFunction = new BigIntFunction();
+ AviatorJavaType aviatorJavaType = new AviatorJavaType("var");
+ Map env = new HashMap<>();
+ env.put("var", true);
- bigIntFunction.call(env, aviatorJavaType);
- }
+ bigIntFunction.call(env, aviatorJavaType);
+ }
}
diff --git a/src/test/java/com/googlecode/aviator/runtime/function/system/DoubleFunctionUnitTest.java b/src/test/java/com/googlecode/aviator/runtime/function/system/DoubleFunctionUnitTest.java
index 2c153ae7..3c7e2d59 100644
--- a/src/test/java/com/googlecode/aviator/runtime/function/system/DoubleFunctionUnitTest.java
+++ b/src/test/java/com/googlecode/aviator/runtime/function/system/DoubleFunctionUnitTest.java
@@ -2,26 +2,25 @@
import com.googlecode.aviator.runtime.type.AviatorJavaType;
import org.junit.Test;
-
import java.util.HashMap;
import java.util.Map;
public class DoubleFunctionUnitTest {
- @Test(expected = ClassCastException.class)
- public void testCall_WithJavaTypeNullArgument() {
- DoubleFunction doubleFunction = new DoubleFunction();
- AviatorJavaType aviatorJavaType = new AviatorJavaType("var");
- doubleFunction.call(null, aviatorJavaType);
- }
+ @Test(expected = ClassCastException.class)
+ public void testCall_WithJavaTypeNullArgument() {
+ DoubleFunction doubleFunction = new DoubleFunction();
+ AviatorJavaType aviatorJavaType = new AviatorJavaType("var");
+ doubleFunction.call(null, aviatorJavaType);
+ }
- @Test(expected = ClassCastException.class)
- public void testCall_WithJavaTypeNotSupportArgument() {
- DoubleFunction doubleFunction = new DoubleFunction();
- AviatorJavaType aviatorJavaType = new AviatorJavaType("var");
- Map env = new HashMap<>();
- env.put("var", true);
+ @Test(expected = ClassCastException.class)
+ public void testCall_WithJavaTypeNotSupportArgument() {
+ DoubleFunction doubleFunction = new DoubleFunction();
+ AviatorJavaType aviatorJavaType = new AviatorJavaType("var");
+ Map env = new HashMap<>();
+ env.put("var", true);
- doubleFunction.call(env, aviatorJavaType);
- }
+ doubleFunction.call(env, aviatorJavaType);
+ }
}
diff --git a/src/test/java/com/googlecode/aviator/runtime/function/system/LongFunctionUnitTest.java b/src/test/java/com/googlecode/aviator/runtime/function/system/LongFunctionUnitTest.java
index 106f22ed..9865a8e2 100644
--- a/src/test/java/com/googlecode/aviator/runtime/function/system/LongFunctionUnitTest.java
+++ b/src/test/java/com/googlecode/aviator/runtime/function/system/LongFunctionUnitTest.java
@@ -2,26 +2,25 @@
import com.googlecode.aviator.runtime.type.AviatorJavaType;
import org.junit.Test;
-
import java.util.HashMap;
import java.util.Map;
public class LongFunctionUnitTest {
- @Test(expected = ClassCastException.class)
- public void testCall_WithJavaTypeNullArgument() {
- LongFunction longFunction = new LongFunction();
- AviatorJavaType aviatorJavaType = new AviatorJavaType("var");
- longFunction.call(null, aviatorJavaType);
- }
+ @Test(expected = ClassCastException.class)
+ public void testCall_WithJavaTypeNullArgument() {
+ LongFunction longFunction = new LongFunction();
+ AviatorJavaType aviatorJavaType = new AviatorJavaType("var");
+ longFunction.call(null, aviatorJavaType);
+ }
- @Test(expected = ClassCastException.class)
- public void testCall_WithJavaTypeNotSupportArgument() {
- LongFunction longFunction = new LongFunction();
- AviatorJavaType aviatorJavaType = new AviatorJavaType("var");
- Map env = new HashMap<>();
- env.put("var", true);
+ @Test(expected = ClassCastException.class)
+ public void testCall_WithJavaTypeNotSupportArgument() {
+ LongFunction longFunction = new LongFunction();
+ AviatorJavaType aviatorJavaType = new AviatorJavaType("var");
+ Map env = new HashMap<>();
+ env.put("var", true);
- longFunction.call(env, aviatorJavaType);
- }
+ longFunction.call(env, aviatorJavaType);
+ }
}