Skip to content

Commit

Permalink
Merge pull request #629 from killme2008/feature/v5.5
Browse files Browse the repository at this point in the history
feat: v.5.4.2 dev branch
  • Loading branch information
killme2008 authored Jun 7, 2024
2 parents 6426d87 + d6e7022 commit 63db445
Show file tree
Hide file tree
Showing 31 changed files with 317 additions and 72 deletions.
10 changes: 5 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
<artifactId>aviator</artifactId>
<version>5.4.2-SNAPSHOT</version>
<name>aviator</name>
<description>A lightweight, high performance expression evaluator for java</description>
<description>A high performance scripting language hosted on the JVM</description>
<url>https://github.com/killme2008/aviator</url>
<inceptionYear>2010</inceptionYear>

<developers>
<developer>
<name>dennis zhuang</name>
<url>http://fnil.net/</url>
<url>https://github.com/killme2008</url>
<timezone>8</timezone>
</developer>
</developers>
Expand Down Expand Up @@ -51,7 +51,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
<version>5.3.36</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand Down Expand Up @@ -118,8 +118,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,10 +404,11 @@ private Map<String, Object> loadScript0(final String abPath) throws IOException
@SuppressWarnings("unchecked")
private Map<String, Object> executeModule(final Expression exp, final String abPath) {
final Env exports = new Env();
exports.configure(this, exp, -1);
final Map<String, Object> module = exp.newEnv("exports", exports, "path", abPath);
Map<String, Object> env = exp.newEnv("__MODULE__", module, "exports", exports);
exp.execute(env);
exports.configure(this, exp);
((BaseExpression) exp).execute(env, false);

return (Map<String, Object>) module.get("exports");
}

Expand Down
30 changes: 23 additions & 7 deletions src/main/java/com/googlecode/aviator/BaseExpression.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -241,10 +243,14 @@ public Map<String, Object> newEnv(final Object... args) {

@Override
public Object execute(Map<String, Object> map) {
return this.execute(map, true);
}

protected Object execute(Map<String, Object> 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);
Expand Down Expand Up @@ -327,23 +333,25 @@ public List<String> getVariableNames() {
return this.varNames;
}

protected Env newEnv(final Map<String, Object> map, final boolean direct) {
protected Env newEnv(final Map<String, Object> map, final boolean direct,
boolean checkExecutionTimeout) {
Env env;
if (direct) {
env = new Env(map, map == Collections.EMPTY_MAP ? new HashMap<String, Object>() : 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<String, Object> map) {
protected Env genTopEnv(final Map<String, Object> 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);
Expand All @@ -354,8 +362,16 @@ protected Env genTopEnv(final Map<String, Object> 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<String, Object> map) {
return newEnv(map, false);
return newEnv(map, false, true);
}

public Map<String, LambdaFunctionBootstrap> getLambdaBootstraps() {
Expand Down
40 changes: 38 additions & 2 deletions src/main/java/com/googlecode/aviator/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.math.MathContext;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import com.googlecode.aviator.utils.Utils;


Expand Down Expand Up @@ -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). <br/>
* <br/>
* Note: this limitation is not strict and may hurt performance, it is only checked before:
* <ul>
* <li>Operator evaluating, such as add, sub etc.</li>
* <li>Jumping in branches, such as loop and conditional clauses etc.</li>
* <li>Function invocation</li>
* </ul>
*
* 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;


/**
Expand All @@ -135,6 +157,8 @@ public static class Value {
public Set<Feature> featureSet;
public Set<Class<?>> classes;
public EvalMode evalMode;
// Temporal cached number value to avoid expensive calculation.
public long cachedNumber;

public Value(final EvalMode evalMode) {
super();
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
Expand All @@ -484,6 +486,7 @@ private Label peekLabel1() {

@Override
public void onTernaryRight(final Token<?> lookhead) {
checkExecutionTimeout();
visitLabel(popLabel1());
visitLineNumber(lookhead);
this.popOperand(); // pop one boolean
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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<String, Integer> name2Index = this.labelNameIndexMap.get(this.currentLabel);
// Is it stored in local?
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/googlecode/aviator/code/interpreter/IR.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ public void eval(final InterpretContext context) {
context.dispatch();
}


@Override
public boolean mayBeCost() {
return true;
}

public OperatorType getOp() {
return this.op;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 ? "<top>" : this.name) + ", " + this.arity + ", "
Expand Down
Loading

0 comments on commit 63db445

Please sign in to comment.