Skip to content

Commit 2968544

Browse files
authored
Merge pull request #631 from killme2008/feat/get-function-names
feat: adds Expression#getFunctionNames
2 parents 63990cf + 0ec087c commit 2968544

14 files changed

Lines changed: 249 additions & 40 deletions

src/main/java/com/googlecode/aviator/AviatorEvaluator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ public static AviatorFunction removeOpFunction(final OperatorType opType) {
430430

431431

432432
/**
433-
* Check if the function is existed in aviator
433+
* Check if the function exists in the global evaluator instance.
434434
*
435435
* @param name
436436
* @return

src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,13 +1327,24 @@ public AviatorFunction removeOpFunction(final OperatorType opType) {
13271327
}
13281328

13291329
/**
1330-
* Check if the function is existed in aviator
1330+
* Check if the function exists in the evaluator. Note: it doesn't check the runtime defined
1331+
* functions.
13311332
*
13321333
* @param name
13331334
* @return
13341335
*/
13351336
public boolean containsFunction(final String name) {
1336-
return this.funcMap.containsKey(name);
1337+
boolean exists = this.funcMap.containsKey(name);
1338+
if (!exists && this.functionLoaders != null) {
1339+
for (FunctionLoader loader : this.functionLoaders) {
1340+
if (loader != null && loader.onFunctionNotFound(name) != null) {
1341+
exists = true;
1342+
break;
1343+
}
1344+
}
1345+
}
1346+
1347+
return exists;
13371348
}
13381349

13391350
/**

src/main/java/com/googlecode/aviator/BaseExpression.java

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ public abstract class BaseExpression implements Expression {
4242
private static final long serialVersionUID = 2819544277750883372L;
4343

4444
public static final String FUNC_PARAMS_VAR = "__funcs_args__";
45-
protected List<String> varNames;
46-
protected List<String> varFullNames;
45+
protected transient List<String> varNames;
46+
protected transient List<String> varFullNames;
4747
private List<VariableMeta> vars;
4848
private String expression;
4949
protected transient AviatorEvaluatorInstance instance;
@@ -53,6 +53,12 @@ public abstract class BaseExpression implements Expression {
5353
// cached compiled string segments for string interpolation.
5454
private transient ConcurrentHashMap<String, FutureTask<StringSegments>> stringSegs =
5555
new ConcurrentHashMap<String, FutureTask<StringSegments>>();
56+
// The function name list in expression
57+
private List<String> functionNames = Collections.emptyList();
58+
59+
// Already filtered function names, try to remove the runtime defined functions.
60+
private transient List<String> filteredFunctionNames = null;
61+
5662

5763
protected String sourceFile;
5864
protected Map<String, LambdaFunctionBootstrap> lambdaBootstraps;
@@ -62,12 +68,12 @@ public String getSourceFile() {
6268
return this.sourceFile;
6369
}
6470

65-
public void setSourceFile(final String sourceFile) {
71+
protected void setSourceFile(final String sourceFile) {
6672
this.sourceFile = sourceFile;
6773
}
6874

6975

70-
public void setInstance(AviatorEvaluatorInstance instance) {
76+
protected void setInstance(AviatorEvaluatorInstance instance) {
7177
this.instance = instance;
7278
}
7379

@@ -264,7 +270,7 @@ protected Object execute(Map<String, Object> map, boolean checkExecutionTimeout)
264270
}
265271
}
266272

267-
public void setFuncsArgs(final Map<Integer, List<FunctionArgument>> funcsArgs) {
273+
protected void setFuncsArgs(final Map<Integer, List<FunctionArgument>> funcsArgs) {
268274
if (funcsArgs != null) {
269275
this.funcsArgs = Collections.unmodifiableMap(funcsArgs);
270276
}
@@ -274,7 +280,7 @@ public Env getCompileEnv() {
274280
return this.compileEnv;
275281
}
276282

277-
public void setCompileEnv(final Env compileEnv) {
283+
protected void setCompileEnv(final Env compileEnv) {
278284
this.compileEnv = compileEnv;
279285
this.compileEnv.setExpression(this);
280286
}
@@ -289,7 +295,7 @@ public String getExpression() {
289295
return this.expression;
290296
}
291297

292-
public void setExpression(final String expression) {
298+
protected void setExpression(final String expression) {
293299
this.expression = expression;
294300
}
295301

@@ -374,11 +380,57 @@ protected Env newEnv(final Map<String, Object> map) {
374380
return newEnv(map, false, true);
375381
}
376382

383+
public List<String> getFunctionNames() {
384+
populateFilteredFuncNames();
385+
386+
return filteredFunctionNames;
387+
}
388+
389+
private void populateFilteredFuncNames() {
390+
if (this.filteredFunctionNames == null) {
391+
Set<String> validNames = new HashSet<String>(this.functionNames.size());
392+
393+
for (String funcName : this.functionNames) {
394+
// Remove internal functions
395+
if (!funcName.startsWith("__") && !funcName.equals("with_meta")) {
396+
validNames.add(funcName);
397+
}
398+
}
399+
400+
Set<String> definedFuncs = new HashSet<String>();
401+
// Find all runtime defined functions
402+
for (VariableMeta v : this.vars) {
403+
// TODO: It's not precise, but could work
404+
if (v.isInit()) {
405+
definedFuncs.add(v.getName());
406+
}
407+
}
408+
409+
if (this.lambdaBootstraps != null) {
410+
// Adds sub-expressions function names
411+
for (LambdaFunctionBootstrap bootstrap : this.lambdaBootstraps.values()) {
412+
validNames.addAll(bootstrap.getExpression().getFunctionNames());
413+
}
414+
}
415+
416+
// Remove runtime defined functions.
417+
validNames.removeAll(definedFuncs);
418+
419+
this.filteredFunctionNames = new ArrayList<>(validNames);
420+
}
421+
}
422+
423+
protected void setFunctionNames(List<String> functionNames) {
424+
if (functionNames != null) {
425+
this.functionNames = functionNames;
426+
}
427+
}
428+
377429
public Map<String, LambdaFunctionBootstrap> getLambdaBootstraps() {
378430
return this.lambdaBootstraps;
379431
}
380432

381-
public void setLambdaBootstraps(final Map<String, LambdaFunctionBootstrap> lambdaBootstraps) {
433+
protected void setLambdaBootstraps(final Map<String, LambdaFunctionBootstrap> lambdaBootstraps) {
382434
this.lambdaBootstraps = lambdaBootstraps;
383435
}
384436

@@ -400,6 +452,7 @@ public void customReadObject(ObjectInputStream input) throws ClassNotFoundExcept
400452
this.sourceFile = (String) input.readObject();
401453
this.lambdaBootstraps = (Map<String, LambdaFunctionBootstrap>) input.readObject();
402454
this.stringSegs = new ConcurrentHashMap<String, FutureTask<StringSegments>>();
455+
this.functionNames = (List<String>) input.readObject();
403456
}
404457

405458
public void customWriteObject(ObjectOutputStream output) throws IOException {
@@ -410,6 +463,7 @@ public void customWriteObject(ObjectOutputStream output) throws IOException {
410463
output.writeObject(this.symbolTable);
411464
output.writeObject(this.sourceFile);
412465
output.writeObject(this.lambdaBootstraps);
466+
output.writeObject(this.functionNames);
413467
}
414468

415469
}

src/main/java/com/googlecode/aviator/Expression.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@
2929
public interface Expression extends Serializable {
3030

3131
/**
32-
* Execute expression with environment
32+
* Execute an expression with an environment, returns the result.
3333
*
3434
* @param env Binding variable environment
35-
* @return
35+
* @return the result of execution
3636
*/
3737
Object execute(Map<String, Object> env);
3838

3939

4040
/**
41-
* Execute expression with empty environment
41+
* Execute an expression with an empty environment, returns the result.
4242
*
43-
* @return
43+
* @return the result of execution
4444
*/
4545
Object execute();
4646

@@ -55,7 +55,7 @@ public interface Expression extends Serializable {
5555

5656
/**
5757
* Returns this expression's all uninitialized global variable names in order when using
58-
* AviatorEvaluator.EVAL mode,else returns empty set
58+
* AviatorEvaluator.EVAL mode, otherwise returns an empty list.
5959
*
6060
* @see com.googlecode.aviator.AviatorEvaluator#EVAL
6161
* @return
@@ -65,7 +65,7 @@ public interface Expression extends Serializable {
6565

6666
/**
6767
* Returns this expression's all uninitialized global variable full names(contains dot) in order
68-
* when using AviatorEvaluator.EVAL mode,else returns empty set
68+
* when using AviatorEvaluator.EVAL mode, otherwise returns an empty list.
6969
*
7070
* @return
7171
*/
@@ -89,4 +89,12 @@ public interface Expression extends Serializable {
8989
*/
9090
String addSymbol(String name);
9191

92+
/**
93+
* Returns the function names in the expression when using AviatorEvaluator.EVAL mode, otherwise
94+
* returns an empty list.
95+
*
96+
* @since 5.4.2
97+
* @return the function name list
98+
*/
99+
List<String> getFunctionNames();
92100
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Copyright (C) 2010 dennis zhuang (killme2008@gmail.com)
3+
*
4+
* This library is free software; you can redistribute it and/or modify it under the terms of the
5+
* GNU Lesser General Public License as published by the Free Software Foundation; either version
6+
* 2.1 of the License, or (at your option) any later version.
7+
*
8+
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
9+
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10+
* Lesser General Public License for more details.
11+
*
12+
* You should have received a copy of the GNU Lesser General Public License along with this program;
13+
* if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
14+
*
15+
**/
16+
package com.googlecode.aviator;
17+
18+
import java.util.List;
19+
import java.util.Map;
20+
import com.googlecode.aviator.runtime.FunctionArgument;
21+
import com.googlecode.aviator.runtime.LambdaFunctionBootstrap;
22+
import com.googlecode.aviator.utils.Env;
23+
24+
/**
25+
* Base expression default methods accessor
26+
*
27+
* @author dennis
28+
*
29+
*/
30+
public class ExpressionAccessor {
31+
32+
public static void setSourceFile(final BaseExpression exp, String sourceFile) {
33+
exp.setSourceFile(sourceFile);
34+
}
35+
36+
public static void setInstance(final BaseExpression exp, AviatorEvaluatorInstance instance) {
37+
exp.setInstance(instance);
38+
}
39+
40+
public static void setCompileEnv(final BaseExpression exp, final Env compileEnv) {
41+
exp.setCompileEnv(compileEnv);
42+
}
43+
44+
public static void setExpression(final BaseExpression exp, final String expression) {
45+
exp.setExpression(expression);
46+
}
47+
48+
public static void setFuncsArgs(final BaseExpression exp,
49+
final Map<Integer, List<FunctionArgument>> funcsArgs) {
50+
exp.setFuncsArgs(funcsArgs);
51+
}
52+
53+
public static void setLambdaBootstraps(final BaseExpression exp,
54+
final Map<String, LambdaFunctionBootstrap> lambdaBootstraps) {
55+
exp.setLambdaBootstraps(lambdaBootstraps);
56+
}
57+
58+
public static void setFunctionNames(final BaseExpression exp, List<String> functionNames) {
59+
exp.setFunctionNames(functionNames);
60+
}
61+
}

src/main/java/com/googlecode/aviator/code/BaseEvalCodeGenerator.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public abstract class BaseEvalCodeGenerator implements EvalCodeGenerator {
4343
*/
4444
protected final Env compileEnv;
4545

46+
protected Map<String, Integer/* counter */> methodTokens = Collections.emptyMap();
47+
4648
protected Map<Integer/* internal function id */, List<FunctionArgument>> getFuncsArgs() {
4749
if (this.funcsArgs == null) {
4850
this.funcsArgs = new HashMap<>();
@@ -54,6 +56,11 @@ protected int getNextFuncInvocationId() {
5456
return this.funcInvocationId++;
5557
}
5658

59+
@Override
60+
public void initMethods(Map<String, Integer> methods) {
61+
this.methodTokens = methods;
62+
}
63+
5764
@Override
5865
public void setParser(final Parser parser) {
5966
this.parser = parser;

src/main/java/com/googlecode/aviator/code/OptimizeCodeGenerator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.googlecode.aviator.AviatorEvaluatorInstance;
2929
import com.googlecode.aviator.BaseExpression;
3030
import com.googlecode.aviator.Expression;
31+
import com.googlecode.aviator.ExpressionAccessor;
3132
import com.googlecode.aviator.Feature;
3233
import com.googlecode.aviator.LiteralExpression;
3334
import com.googlecode.aviator.exception.CompileExpressionErrorException;
@@ -448,8 +449,8 @@ public Expression getResult(final boolean unboxObject) {
448449

449450

450451
if (exp instanceof BaseExpression) {
451-
((BaseExpression) exp).setCompileEnv(getCompileEnv());
452-
((BaseExpression) exp).setSourceFile(this.sourceFile);
452+
ExpressionAccessor.setCompileEnv((BaseExpression) exp, getCompileEnv());
453+
ExpressionAccessor.setSourceFile((BaseExpression) exp, this.sourceFile);
453454
}
454455
return exp;
455456
}

src/main/java/com/googlecode/aviator/code/asm/ASMCodeGenerator.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import com.googlecode.aviator.AviatorEvaluatorInstance;
5959
import com.googlecode.aviator.ClassExpression;
6060
import com.googlecode.aviator.Expression;
61+
import com.googlecode.aviator.ExpressionAccessor;
6162
import com.googlecode.aviator.Options;
6263
import com.googlecode.aviator.asm.ClassWriter;
6364
import com.googlecode.aviator.asm.Label;
@@ -127,8 +128,6 @@ public class ASMCodeGenerator extends BaseEvalCodeGenerator {
127128
private Map<Token<?>/* constant token */, String/* field name */> constantPool =
128129
Collections.emptyMap();
129130

130-
private Map<String, Integer/* counter */> methodTokens = Collections.emptyMap();
131-
132131
private final Map<Label, Map<String/* inner name */, Integer/* local index */>> labelNameIndexMap =
133132
new IdentityHashMap<>();
134133
private static final Label START_LABEL = new Label();
@@ -730,9 +729,10 @@ public Expression getResult(final boolean unboxObject) {
730729
defineClass.getConstructor(AviatorEvaluatorInstance.class, List.class, SymbolTable.class);
731730
ClassExpression exp = (ClassExpression) constructor.newInstance(this.instance,
732731
new ArrayList<VariableMeta>(this.variables.values()), this.symbolTable);
733-
exp.setLambdaBootstraps(this.lambdaBootstraps);
734-
exp.setFuncsArgs(this.funcsArgs);
735-
exp.setSourceFile(this.sourceFile);
732+
ExpressionAccessor.setLambdaBootstraps(exp, this.lambdaBootstraps);
733+
ExpressionAccessor.setFuncsArgs(exp, this.funcsArgs);
734+
ExpressionAccessor.setSourceFile(exp, this.sourceFile);
735+
ExpressionAccessor.setFunctionNames(exp, new ArrayList<>(this.methodTokens.keySet()));
736736
if (enableSerializable) {
737737
exp.setClassBytes(bytes);
738738
}
@@ -1048,7 +1048,7 @@ public void initConstants(final Set<Token<?>> constants) {
10481048

10491049
@Override
10501050
public void initMethods(final Map<String, Integer/* counter */> methods) {
1051-
this.methodTokens = methods;
1051+
super.initMethods(methods);
10521052
this.innerMethodMap = new HashMap<>(methods.size());
10531053
for (String outterMethodName : methods.keySet()) {
10541054
// Use inner method name instead of outter method name

0 commit comments

Comments
 (0)