From 079c3ab202ca771b3bc536d26615582f2a977b29 Mon Sep 17 00:00:00 2001 From: dennis Date: Thu, 4 Apr 2019 11:47:13 +0800 Subject: [PATCH 1/8] (feat) rename vars --- src/main/java/com/googlecode/aviator/Options.java | 4 +++- .../java/com/googlecode/aviator/lexer/ExpressionLexer.java | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/googlecode/aviator/Options.java b/src/main/java/com/googlecode/aviator/Options.java index b9e8f39a..35f390f5 100644 --- a/src/main/java/com/googlecode/aviator/Options.java +++ b/src/main/java/com/googlecode/aviator/Options.java @@ -46,7 +46,9 @@ public enum Options { */ ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, /** - * When true, always parsing long number into BigDecial, default is false. + * When true, always parsing integral number into BigDecial, default is false. + * + * @since 4.2.0 */ ALWAYS_PARSE_INTEGRAL_NUMBER_INTO_DECIMAL, /** diff --git a/src/main/java/com/googlecode/aviator/lexer/ExpressionLexer.java b/src/main/java/com/googlecode/aviator/lexer/ExpressionLexer.java index ab803fc2..30c67688 100644 --- a/src/main/java/com/googlecode/aviator/lexer/ExpressionLexer.java +++ b/src/main/java/com/googlecode/aviator/lexer/ExpressionLexer.java @@ -51,7 +51,7 @@ public class ExpressionLexer { private String expression; private MathContext mathContext; private boolean parseFloatIntoDecimal; - private boolean parseLongIntoDecimal; + private boolean parseIntegralNumberIntoDecimal; public ExpressionLexer(AviatorEvaluatorInstance instance, String expression) { this.iterator = new StringCharacterIterator(expression); @@ -62,7 +62,7 @@ public ExpressionLexer(AviatorEvaluatorInstance instance, String expression) { this.mathContext = this.instance.getOptionValue(Options.MATH_CONTEXT).mathContext; this.parseFloatIntoDecimal = this.instance.getOptionValue(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL).bool; - this.parseLongIntoDecimal = + this.parseIntegralNumberIntoDecimal = this.instance.getOptionValue(Options.ALWAYS_PARSE_INTEGRAL_NUMBER_INTO_DECIMAL).bool; } @@ -256,7 +256,7 @@ public Token scan(boolean analyse) { value = dval; } } else { - if (this.parseLongIntoDecimal) { + if (this.parseIntegralNumberIntoDecimal) { // we make integral number as a BigDecimal. value = new BigDecimal(sb.toString(), this.mathContext); } else { From f8a5082c276b8f24173e36323427aec8312f3e42 Mon Sep 17 00:00:00 2001 From: dennis Date: Mon, 20 May 2019 20:06:17 +0800 Subject: [PATCH 2/8] (feat) Adds OperatorOverloadExample --- .../example/OperatorOverloadExample.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/test/java/com/googlecode/aviator/example/OperatorOverloadExample.java diff --git a/src/test/java/com/googlecode/aviator/example/OperatorOverloadExample.java b/src/test/java/com/googlecode/aviator/example/OperatorOverloadExample.java new file mode 100644 index 00000000..4e8de63b --- /dev/null +++ b/src/test/java/com/googlecode/aviator/example/OperatorOverloadExample.java @@ -0,0 +1,48 @@ +package com.googlecode.aviator.example; + +import java.math.BigDecimal; +import java.util.Map; +import com.googlecode.aviator.AviatorEvaluator; +import com.googlecode.aviator.lexer.token.OperatorType; +import com.googlecode.aviator.runtime.RuntimeUtils; +import com.googlecode.aviator.runtime.function.AbstractFunction; +import com.googlecode.aviator.runtime.function.FunctionUtils; +import com.googlecode.aviator.runtime.type.AviatorDecimal; +import com.googlecode.aviator.runtime.type.AviatorNumber; +import com.googlecode.aviator.runtime.type.AviatorObject; + +public class OperatorOverloadExample { + + /** + * Overload divide operator, when the dividend is zero,return 0. + * + * @author boyan(boyan@antfin.com) + * + */ + public static class OverloadDivide extends AbstractFunction { + + @Override + public String getName() { + return "/"; + } + + @Override + public AviatorObject call(final Map env, final AviatorObject arg1, + final AviatorObject arg2) { + if (FunctionUtils.getNumberValue(arg2, env).doubleValue() == 0) { + return new AviatorDecimal(0); + } + BigDecimal left = AviatorNumber.valueOf(arg1.getValue(env)).toDecimal(env); + BigDecimal right = AviatorNumber.valueOf(arg2.getValue(env)).toDecimal(env); + return AviatorDecimal.valueOf(left.divide(right, RuntimeUtils.getMathContext(env))); + } + + } + + public static void main(final String[] args) { + AviatorEvaluator.addOpFunction(OperatorType.DIV, new OverloadDivide()); + + System.out.println(AviatorEvaluator.execute("4 - 1/0 + 3")); + System.out.println(AviatorEvaluator.execute("4 - 1/2.0 + 3")); + } +} From 445288f12e9d751879dd66f8753b88ae9fd46b77 Mon Sep 17 00:00:00 2001 From: dennis Date: Thu, 23 May 2019 11:21:47 +0800 Subject: [PATCH 3/8] (fix) issue #134 --- .../runtime/function/LambdaFunction.java | 14 +++++---- .../aviator/test/function/FunctionTest.java | 31 +++++++++++++------ 2 files changed, 29 insertions(+), 16 deletions(-) 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 758ef48f..db40b52b 100644 --- a/src/main/java/com/googlecode/aviator/runtime/function/LambdaFunction.java +++ b/src/main/java/com/googlecode/aviator/runtime/function/LambdaFunction.java @@ -26,7 +26,8 @@ public abstract class LambdaFunction extends AbstractFunction { // closure context protected Env context; - public LambdaFunction(List arguments, Expression expression, Env context) { + public LambdaFunction(final List arguments, final Expression expression, + final Env context) { super(); this.arguments = arguments; this.context = context; @@ -40,13 +41,14 @@ public LambdaFunction(List arguments, Expression expression, Env context this.expression = expression; } - protected Map newEnv(Map parentEnv, AviatorObject... args) { - Env env = new Env(new Env(parentEnv, context)); - env.setInstance(context.getInstance()); + protected Map newEnv(final Map parentEnv, + final AviatorObject... args) { + Env env = new Env(new Env(parentEnv, this.context)); + env.setInstance(this.context.getInstance()); int i = 0; - for (String name : arguments) { - env.put(name, args[i++].getValue(env)); + for (String name : this.arguments) { + env.put(name, args[i++].getValue(parentEnv)); } return env; } diff --git a/src/test/java/com/googlecode/aviator/test/function/FunctionTest.java b/src/test/java/com/googlecode/aviator/test/function/FunctionTest.java index 3e27f7d5..4f895a81 100644 --- a/src/test/java/com/googlecode/aviator/test/function/FunctionTest.java +++ b/src/test/java/com/googlecode/aviator/test/function/FunctionTest.java @@ -901,7 +901,7 @@ public void testSeqMinMaxFunction() { } - private List newList(Object... args) { + private List newList(final Object... args) { List list = new ArrayList<>(); for (Object obj : args) { list.add(obj); @@ -909,7 +909,7 @@ private List newList(Object... args) { return list; } - private Set newSet(Object... args) { + private Set newSet(final Object... args) { Set list = new HashSet<>(); for (Object obj : args) { list.add(obj); @@ -983,6 +983,17 @@ public void testSeqNewMap() { assertEquals("b", AviatorEvaluator.execute("seq.get(seq.map(1,2,3,4,'a','b'), 'a')")); } + @Test + public void testIssue134() { + Map env = new HashMap<>(2); + env.put("v", 3); + assertEquals(3, AviatorEvaluator + .execute("func=lambda(v)->v+2 end;func2=lambda(v)->func(v) end;func(1) ; func2(1)", env)); + assertEquals(6, AviatorEvaluator.execute( + "func=lambda(v)->v+2 end;func2=lambda(v)->func(v) end; func3 = lambda(v) -> func(v) + func2(v) end; func(1); func2(1);func3(1)", + env)); + } + @Test public void testSeqNewSet() { assertEquals(newSet(), AviatorEvaluator.execute("seq.set()")); @@ -1062,39 +1073,39 @@ public static class User { private Integer age; private String name; - public User(Long id, Integer age, String name) { + public User(final Long id, final Integer age, final String name) { this.id = id; this.age = age; this.name = name; } public Long getId() { - return id; + return this.id; } - public void setId(Long id) { + public void setId(final Long id) { this.id = id; } public Integer getAge() { - return age; + return this.age; } - public void setAge(Integer age) { + public void setAge(final Integer age) { this.age = age; } public String getName() { - return name; + return this.name; } - public void setName(String name) { + public void setName(final String name) { this.name = name; } @Override public String toString() { - return "User{" + "id=" + id + ", age=" + age + ", name='" + name + '\'' + '}'; + return "User{" + "id=" + this.id + ", age=" + this.age + ", name='" + this.name + '\'' + '}'; } } } From e7440af411a63ae7ef706a61b0f4fb56bb54cd5c Mon Sep 17 00:00:00 2001 From: dennis Date: Sun, 26 May 2019 15:29:10 +0800 Subject: [PATCH 4/8] (feat) Impl capturing function arguments, #128, #131 --- .../googlecode/aviator/BaseExpression.java | 33 +- .../java/com/googlecode/aviator/Options.java | 32 +- .../aviator/code/CodeGenerator.java | 4 +- .../aviator/code/LambdaGenerator.java | 209 ++++---- .../aviator/code/OptimizeCodeGenerator.java | 160 +++--- .../aviator/code/asm/ASMCodeGenerator.java | 363 +++++++------ .../aviator/lexer/ExpressionLexer.java | 78 +-- .../aviator/lexer/token/OperatorToken.java | 27 +- .../aviator/lexer/token/Variable.java | 17 +- .../aviator/parser/ExpressionParser.java | 493 ++++++++++-------- .../aviator/runtime/FunctionArgument.java | 45 ++ .../runtime/function/FunctionUtils.java | 36 +- .../aviator/runtime/type/AviatorFunction.java | 9 +- .../com/googlecode/aviator/utils/Env.java | 81 +-- .../code/asm/ASMCodeGeneratorUnitTest.java | 122 ++--- .../parser/ExpressionParserUnitTest.java | 138 +++-- .../aviator/parser/FakeCodeGenerator.java | 152 +++--- .../aviator/test/function/FunctionTest.java | 76 +++ 18 files changed, 1212 insertions(+), 863 deletions(-) create mode 100644 src/main/java/com/googlecode/aviator/runtime/FunctionArgument.java diff --git a/src/main/java/com/googlecode/aviator/BaseExpression.java b/src/main/java/com/googlecode/aviator/BaseExpression.java index 42b2dc43..161ea027 100644 --- a/src/main/java/com/googlecode/aviator/BaseExpression.java +++ b/src/main/java/com/googlecode/aviator/BaseExpression.java @@ -6,6 +6,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import com.googlecode.aviator.runtime.FunctionArgument; import com.googlecode.aviator.utils.Env; @@ -17,15 +18,16 @@ */ public abstract class BaseExpression implements Expression { - private List varNames; - private List varFullNames; + public static final String FUNC_PARAMS_VAR = "__funcs_args__"; + private final List varNames; + private final List varFullNames; private String expression; protected AviatorEvaluatorInstance instance; private Env compileEnv; + private Map> funcsArgs = Collections.emptyMap(); - - public BaseExpression(AviatorEvaluatorInstance instance, List varNames) { + public BaseExpression(final AviatorEvaluatorInstance instance, final List varNames) { super(); this.varFullNames = varNames; this.instance = instance; @@ -41,19 +43,23 @@ public BaseExpression(AviatorEvaluatorInstance instance, List varNames) } + public void setFuncsArgs(final Map> funcsArgs) { + if (funcsArgs != null) { + this.funcsArgs = funcsArgs; + } + } + public Env getCompileEnv() { - return compileEnv; + return this.compileEnv; } - - public void setCompileEnv(Env compileEnv) { + public void setCompileEnv(final Env compileEnv) { this.compileEnv = compileEnv; } - /** * Returns the expression string when turn on {@link Options#TRACE_EVAL} option, else returns * null. @@ -64,7 +70,7 @@ public String getExpression() { return this.expression; } - public void setExpression(String expression) { + public void setExpression(final String expression) { this.expression = expression; } @@ -96,7 +102,7 @@ public List getVariableNames() { return this.varNames; } - protected Env newEnv(Map map, boolean direct) { + protected Env newEnv(final Map map, final boolean direct) { Env env; if (direct) { env = new Env(map, map == Collections.EMPTY_MAP ? new HashMap() : map); @@ -107,16 +113,19 @@ protected Env newEnv(Map map, boolean direct) { return env; } - protected Env genTopEnv(Map map) { + protected Env genTopEnv(final Map map) { Env env = newEnv(map, this.instance.getOptionValue(Options.USE_USER_ENV_AS_TOP_ENV_DIRECTLY).bool); if (this.compileEnv != null && !this.compileEnv.isEmpty()) { env.putAll(this.compileEnv); } + if (!this.funcsArgs.isEmpty()) { + env.put(FUNC_PARAMS_VAR, this.funcsArgs); + } return env; } - protected Env newEnv(Map map) { + protected Env newEnv(final Map map) { return newEnv(map, false); } diff --git a/src/main/java/com/googlecode/aviator/Options.java b/src/main/java/com/googlecode/aviator/Options.java index 35f390f5..2f24700f 100644 --- a/src/main/java/com/googlecode/aviator/Options.java +++ b/src/main/java/com/googlecode/aviator/Options.java @@ -47,7 +47,7 @@ public enum Options { ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, /** * When true, always parsing integral number into BigDecial, default is false. - * + * * @since 4.2.0 */ ALWAYS_PARSE_INTEGRAL_NUMBER_INTO_DECIMAL, @@ -62,6 +62,14 @@ public enum Options { */ PUT_CAPTURING_GROUPS_INTO_ENV, + /** + * Whether to capture the function arguments(invocation) into env, the parameter list will be + * stored in __args__ variable in function body. Default is false(disabled). + * + * @since 4.2.0 + */ + CAPTURE_FUNCTION_ARGS, + /** * Enable property access syntax sugar, use common-beantuils to access property such as "a.b.c" * etc. Default value is true, enable this behaviour. @@ -107,24 +115,25 @@ public static class Value { public MathContext mathContext; public int level; - public Value(boolean bool) { + public Value(final boolean bool) { super(); this.bool = bool; } - public Value(MathContext mathContext) { + public Value(final MathContext mathContext) { super(); this.mathContext = mathContext; } - public Value(int level) { + public Value(final int level) { super(); this.level = level; } @Override public String toString() { - return "Value [bool=" + bool + ", mathContext=" + mathContext + ", level=" + level + "]"; + return "Value [bool=" + this.bool + ", mathContext=" + this.mathContext + ", level=" + + this.level + "]"; } @@ -136,7 +145,7 @@ public String toString() { * @param val * @return */ - public Object intoObject(Value val) { + public Object intoObject(final Value val) { switch (this) { case ALWAYS_USE_DOUBLE_AS_DECIMAL: case ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL: @@ -148,6 +157,7 @@ public Object intoObject(Value val) { case NIL_WHEN_PROPERTY_NOT_FOUND: case USE_USER_ENV_AS_TOP_ENV_DIRECTLY: case DISABLE_ASSIGNMENT: + case CAPTURE_FUNCTION_ARGS: return val.bool; case OPTIMIZE_LEVEL: { return val.level; @@ -164,7 +174,7 @@ public Object intoObject(Value val) { * @param val * @return */ - public Value intoValue(Object val) { + public Value intoValue(final Object val) { switch (this) { case ALWAYS_USE_DOUBLE_AS_DECIMAL: case ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL: @@ -175,6 +185,7 @@ public Value intoValue(Object val) { case ENABLE_PROPERTY_SYNTAX_SUGAR: case NIL_WHEN_PROPERTY_NOT_FOUND: case USE_USER_ENV_AS_TOP_ENV_DIRECTLY: + case CAPTURE_FUNCTION_ARGS: case DISABLE_ASSIGNMENT: return ((boolean) val) ? TRUE_VALUE : FALSE_VALUE; case OPTIMIZE_LEVEL: { @@ -191,7 +202,7 @@ public Value intoValue(Object val) { throw new IllegalArgumentException("Fail to cast value " + val + " for option " + this); } - public boolean isValidValue(Object val) { + public boolean isValidValue(final Object val) { switch (this) { case ALWAYS_USE_DOUBLE_AS_DECIMAL: case ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL: @@ -203,6 +214,7 @@ public boolean isValidValue(Object val) { case NIL_WHEN_PROPERTY_NOT_FOUND: case USE_USER_ENV_AS_TOP_ENV_DIRECTLY: case DISABLE_ASSIGNMENT: + case CAPTURE_FUNCTION_ARGS: return val instanceof Boolean; case OPTIMIZE_LEVEL: return val instanceof Integer && (((Integer) val).intValue() == AviatorEvaluator.EVAL @@ -230,7 +242,7 @@ public boolean isValidValue(Object val) { * @return */ public Object getDefaultValue() { - return this.intoObject(this.getDefaultValueObject()); + return intoObject(getDefaultValueObject()); } @@ -261,6 +273,8 @@ public Value getDefaultValueObject() { return TRUE_VALUE; case USE_USER_ENV_AS_TOP_ENV_DIRECTLY: return TRUE_VALUE; + case CAPTURE_FUNCTION_ARGS: + return FALSE_VALUE; case DISABLE_ASSIGNMENT: return FALSE_VALUE; } diff --git a/src/main/java/com/googlecode/aviator/code/CodeGenerator.java b/src/main/java/com/googlecode/aviator/code/CodeGenerator.java index 3d441e10..6cd19b34 100644 --- a/src/main/java/com/googlecode/aviator/code/CodeGenerator.java +++ b/src/main/java/com/googlecode/aviator/code/CodeGenerator.java @@ -15,9 +15,11 @@ **/ package com.googlecode.aviator.code; +import java.util.List; import com.googlecode.aviator.Expression; import com.googlecode.aviator.lexer.token.Token; import com.googlecode.aviator.parser.Parser; +import com.googlecode.aviator.runtime.FunctionArgument; /** @@ -125,7 +127,7 @@ public interface CodeGenerator { public void onMethodParameter(Token lookhead); - public void onMethodInvoke(Token lookhead); + public void onMethodInvoke(Token lookhead, List params); public void onLambdaDefineStart(Token lookhead); diff --git a/src/main/java/com/googlecode/aviator/code/LambdaGenerator.java b/src/main/java/com/googlecode/aviator/code/LambdaGenerator.java index 60be1b21..9951eb5e 100644 --- a/src/main/java/com/googlecode/aviator/code/LambdaGenerator.java +++ b/src/main/java/com/googlecode/aviator/code/LambdaGenerator.java @@ -36,6 +36,7 @@ import com.googlecode.aviator.parser.AviatorClassLoader; import com.googlecode.aviator.parser.Parser; import com.googlecode.aviator.parser.ScopeInfo; +import com.googlecode.aviator.runtime.FunctionArgument; import com.googlecode.aviator.runtime.LambdaFunctionBootstrap; import com.googlecode.aviator.runtime.function.LambdaFunction; import com.googlecode.aviator.utils.Env; @@ -47,19 +48,20 @@ * */ public class LambdaGenerator implements CodeGenerator { - private ClassWriter classWriter; - private List arguments; - private CodeGenerator codeGenerator; - private CodeGenerator parentCodeGenerator; - private AviatorClassLoader classLoader; - private AviatorEvaluatorInstance instance; - private String className; + private final ClassWriter classWriter; + private final List arguments; + private final CodeGenerator codeGenerator; + private final CodeGenerator parentCodeGenerator; + private final AviatorClassLoader classLoader; + private final AviatorEvaluatorInstance instance; + private final String className; private static final AtomicLong LAMBDA_COUNTER = new AtomicLong(); private MethodVisitor mv; private ScopeInfo scopeInfo; - public LambdaGenerator(AviatorEvaluatorInstance instance, CodeGenerator parentCodeGenerator, - Parser parser, AviatorClassLoader classLoader) { + public LambdaGenerator(final AviatorEvaluatorInstance instance, + final CodeGenerator parentCodeGenerator, final Parser parser, + final AviatorClassLoader classLoader) { this.arguments = new ArrayList(); this.instance = instance; this.parentCodeGenerator = parentCodeGenerator; @@ -71,24 +73,24 @@ public LambdaGenerator(AviatorEvaluatorInstance instance, CodeGenerator parentCo "Lambda_" + System.currentTimeMillis() + "_" + LAMBDA_COUNTER.getAndIncrement(); // Auto compute frames this.classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - this.visitClass(); - this.makeConstructor(); - this.makeGetName(); + visitClass(); + makeConstructor(); + makeGetName(); } public ScopeInfo getScopeInfo() { - return scopeInfo; + return this.scopeInfo; } - public void setScopeInfo(ScopeInfo scopeInfo) { + public void setScopeInfo(final ScopeInfo scopeInfo) { this.scopeInfo = scopeInfo; } @Override - public void setParser(Parser parser) { + public void setParser(final Parser parser) { this.codeGenerator.setParser(parser); } @@ -191,8 +193,8 @@ public void compileCallMethod() { } private void visitClass() { - this.classWriter.visit(instance.getBytecodeVersion(), ACC_PUBLIC + ACC_SUPER, this.className, - null, "com/googlecode/aviator/runtime/function/LambdaFunction", null); + this.classWriter.visit(this.instance.getBytecodeVersion(), ACC_PUBLIC + ACC_SUPER, + this.className, null, "com/googlecode/aviator/runtime/function/LambdaFunction", null); } @@ -202,14 +204,15 @@ private void endVisitClass() { } public LambdaFunctionBootstrap getLmabdaBootstrap() { - Expression expression = this.getResult(); - this.endVisitClass(); + Expression expression = getResult(); + endVisitClass(); byte[] bytes = this.classWriter.toByteArray(); try { Class defineClass = null; if (ClassDefiner.isJDK7()) { - defineClass = ClassDefiner.defineClassByClassLoader(className, bytes, classLoader); + defineClass = + ClassDefiner.defineClassByClassLoader(this.className, bytes, this.classLoader); } else { defineClass = ClassDefiner.defineClass(this.className, LambdaFunction.class, bytes, this.classLoader); @@ -217,296 +220,296 @@ public LambdaFunctionBootstrap getLmabdaBootstrap() { Constructor constructor = defineClass.getConstructor(List.class, Expression.class, Env.class); MethodHandle methodHandle = MethodHandles.lookup().unreflectConstructor(constructor); - return new LambdaFunctionBootstrap(this.className, expression, methodHandle, arguments); + return new LambdaFunctionBootstrap(this.className, expression, methodHandle, this.arguments); } catch (Exception e) { throw new CompileExpressionErrorException("define lambda class error", e); } } - public void addArgument(String name) { + public void addArgument(final String name) { this.arguments.add(name); } @Override - public void onShiftRight(Token lookhead) { - codeGenerator.onShiftRight(lookhead); + public void onShiftRight(final Token lookhead) { + this.codeGenerator.onShiftRight(lookhead); } @Override - public void onShiftLeft(Token lookhead) { - codeGenerator.onShiftLeft(lookhead); + public void onShiftLeft(final Token lookhead) { + this.codeGenerator.onShiftLeft(lookhead); } @Override - public void onUnsignedShiftRight(Token lookhead) { - codeGenerator.onUnsignedShiftRight(lookhead); + public void onUnsignedShiftRight(final Token lookhead) { + this.codeGenerator.onUnsignedShiftRight(lookhead); } @Override - public void onAssignment(Token lookhead) { + public void onAssignment(final Token lookhead) { this.codeGenerator.onAssignment(lookhead); } @Override - public void onBitOr(Token lookhead) { - codeGenerator.onBitOr(lookhead); + public void onBitOr(final Token lookhead) { + this.codeGenerator.onBitOr(lookhead); } @Override - public void onBitAnd(Token lookhead) { - codeGenerator.onBitAnd(lookhead); + public void onBitAnd(final Token lookhead) { + this.codeGenerator.onBitAnd(lookhead); } @Override - public void onBitXor(Token lookhead) { - codeGenerator.onBitXor(lookhead); + public void onBitXor(final Token lookhead) { + this.codeGenerator.onBitXor(lookhead); } @Override - public void onBitNot(Token lookhead) { - codeGenerator.onBitNot(lookhead); + public void onBitNot(final Token lookhead) { + this.codeGenerator.onBitNot(lookhead); } @Override - public void onAdd(Token lookhead) { - codeGenerator.onAdd(lookhead); + public void onAdd(final Token lookhead) { + this.codeGenerator.onAdd(lookhead); } @Override - public void onSub(Token lookhead) { - codeGenerator.onSub(lookhead); + public void onSub(final Token lookhead) { + this.codeGenerator.onSub(lookhead); } @Override - public void onMult(Token lookhead) { - codeGenerator.onMult(lookhead); + public void onMult(final Token lookhead) { + this.codeGenerator.onMult(lookhead); } @Override - public void onDiv(Token lookhead) { - codeGenerator.onDiv(lookhead); + public void onDiv(final Token lookhead) { + this.codeGenerator.onDiv(lookhead); } @Override - public void onAndLeft(Token lookhead) { - codeGenerator.onAndLeft(lookhead); + public void onAndLeft(final Token lookhead) { + this.codeGenerator.onAndLeft(lookhead); } @Override - public void onAndRight(Token lookhead) { - codeGenerator.onAndRight(lookhead); + public void onAndRight(final Token lookhead) { + this.codeGenerator.onAndRight(lookhead); } @Override - public void onTernaryBoolean(Token lookhead) { - codeGenerator.onTernaryBoolean(lookhead); + public void onTernaryBoolean(final Token lookhead) { + this.codeGenerator.onTernaryBoolean(lookhead); } @Override - public void onTernaryLeft(Token lookhead) { - codeGenerator.onTernaryLeft(lookhead); + public void onTernaryLeft(final Token lookhead) { + this.codeGenerator.onTernaryLeft(lookhead); } @Override - public void onTernaryRight(Token lookhead) { - codeGenerator.onTernaryRight(lookhead); + public void onTernaryRight(final Token lookhead) { + this.codeGenerator.onTernaryRight(lookhead); } @Override - public void onTernaryEnd(Token lookhead) { + public void onTernaryEnd(final Token lookhead) { this.codeGenerator.onTernaryEnd(lookhead); } @Override - public void onJoinLeft(Token lookhead) { - codeGenerator.onJoinLeft(lookhead); + public void onJoinLeft(final Token lookhead) { + this.codeGenerator.onJoinLeft(lookhead); } @Override - public void onJoinRight(Token lookhead) { - codeGenerator.onJoinRight(lookhead); + public void onJoinRight(final Token lookhead) { + this.codeGenerator.onJoinRight(lookhead); } @Override - public void onEq(Token lookhead) { - codeGenerator.onEq(lookhead); + public void onEq(final Token lookhead) { + this.codeGenerator.onEq(lookhead); } @Override - public void onMatch(Token lookhead) { - codeGenerator.onMatch(lookhead); + public void onMatch(final Token lookhead) { + this.codeGenerator.onMatch(lookhead); } @Override - public void onNeq(Token lookhead) { - codeGenerator.onNeq(lookhead); + public void onNeq(final Token lookhead) { + this.codeGenerator.onNeq(lookhead); } @Override - public void onLt(Token lookhead) { - codeGenerator.onLt(lookhead); + public void onLt(final Token lookhead) { + this.codeGenerator.onLt(lookhead); } @Override - public void onLe(Token lookhead) { - codeGenerator.onLe(lookhead); + public void onLe(final Token lookhead) { + this.codeGenerator.onLe(lookhead); } @Override - public void onGt(Token lookhead) { - codeGenerator.onGt(lookhead); + public void onGt(final Token lookhead) { + this.codeGenerator.onGt(lookhead); } @Override - public void onGe(Token lookhead) { - codeGenerator.onGe(lookhead); + public void onGe(final Token lookhead) { + this.codeGenerator.onGe(lookhead); } @Override - public void onMod(Token lookhead) { - codeGenerator.onMod(lookhead); + public void onMod(final Token lookhead) { + this.codeGenerator.onMod(lookhead); } @Override - public void onNot(Token lookhead) { - codeGenerator.onNot(lookhead); + public void onNot(final Token lookhead) { + this.codeGenerator.onNot(lookhead); } @Override - public void onNeg(Token lookhead) { - codeGenerator.onNeg(lookhead); + public void onNeg(final Token lookhead) { + this.codeGenerator.onNeg(lookhead); } @Override public Expression getResult() { - return codeGenerator.getResult(); + return this.codeGenerator.getResult(); } @Override - public void onConstant(Token lookhead) { - codeGenerator.onConstant(lookhead); + public void onConstant(final Token lookhead) { + this.codeGenerator.onConstant(lookhead); } @Override - public void onMethodName(Token lookhead) { - codeGenerator.onMethodName(lookhead); + public void onMethodName(final Token lookhead) { + this.codeGenerator.onMethodName(lookhead); } @Override - public void onMethodParameter(Token lookhead) { - codeGenerator.onMethodParameter(lookhead); + public void onMethodParameter(final Token lookhead) { + this.codeGenerator.onMethodParameter(lookhead); } @Override - public void onMethodInvoke(Token lookhead) { - codeGenerator.onMethodInvoke(lookhead); + public void onMethodInvoke(final Token lookhead, final List params) { + this.codeGenerator.onMethodInvoke(lookhead, params); } @Override - public void onLambdaDefineStart(Token lookhead) { - codeGenerator.onLambdaDefineStart(lookhead); + public void onLambdaDefineStart(final Token lookhead) { + this.codeGenerator.onLambdaDefineStart(lookhead); } @Override - public void onLambdaArgument(Token lookhead) { - codeGenerator.onLambdaArgument(lookhead); + public void onLambdaArgument(final Token lookhead) { + this.codeGenerator.onLambdaArgument(lookhead); } @Override - public void onLambdaBodyStart(Token lookhead) { - codeGenerator.onLambdaBodyStart(lookhead); + public void onLambdaBodyStart(final Token lookhead) { + this.codeGenerator.onLambdaBodyStart(lookhead); } @Override - public void onLambdaBodyEnd(Token lookhead) { + public void onLambdaBodyEnd(final Token lookhead) { // should call parent generator - parentCodeGenerator.onLambdaBodyEnd(lookhead); + this.parentCodeGenerator.onLambdaBodyEnd(lookhead); } @Override - public void onArray(Token lookhead) { - codeGenerator.onArray(lookhead); + public void onArray(final Token lookhead) { + this.codeGenerator.onArray(lookhead); } @Override - public void onArrayIndexStart(Token token) { - codeGenerator.onArrayIndexStart(token); + public void onArrayIndexStart(final Token token) { + this.codeGenerator.onArrayIndexStart(token); } @Override - public void onArrayIndexEnd(Token lookhead) { - codeGenerator.onArrayIndexEnd(lookhead); + public void onArrayIndexEnd(final Token lookhead) { + this.codeGenerator.onArrayIndexEnd(lookhead); } } diff --git a/src/main/java/com/googlecode/aviator/code/OptimizeCodeGenerator.java b/src/main/java/com/googlecode/aviator/code/OptimizeCodeGenerator.java index 1c6770cc..c47ae364 100644 --- a/src/main/java/com/googlecode/aviator/code/OptimizeCodeGenerator.java +++ b/src/main/java/com/googlecode/aviator/code/OptimizeCodeGenerator.java @@ -41,6 +41,7 @@ import com.googlecode.aviator.lexer.token.Variable; import com.googlecode.aviator.parser.AviatorClassLoader; import com.googlecode.aviator.parser.Parser; +import com.googlecode.aviator.runtime.FunctionArgument; import com.googlecode.aviator.runtime.LambdaFunctionBootstrap; import com.googlecode.aviator.runtime.op.OperationRuntime; import com.googlecode.aviator.runtime.type.AviatorBoolean; @@ -60,7 +61,7 @@ * */ public class OptimizeCodeGenerator implements CodeGenerator { - private ASMCodeGenerator codeGen; + private final ASMCodeGenerator codeGen; private final List> tokenList = new ArrayList>(); @@ -69,7 +70,7 @@ public class OptimizeCodeGenerator implements CodeGenerator { private CodeGenerator parentCodeGenerator; private boolean trace = false; - private AviatorEvaluatorInstance instance; + private final AviatorEvaluatorInstance instance; // the expression parser private Parser parser; @@ -81,8 +82,8 @@ public class OptimizeCodeGenerator implements CodeGenerator { private Map lambdaBootstraps; - public OptimizeCodeGenerator(AviatorEvaluatorInstance instance, ClassLoader classLoader, - OutputStream traceOutStream, boolean trace) { + public OptimizeCodeGenerator(final AviatorEvaluatorInstance instance, + final ClassLoader classLoader, final OutputStream traceOutStream, final boolean trace) { this.instance = instance; this.codeGen = new ASMCodeGenerator(instance, (AviatorClassLoader) classLoader, traceOutStream, trace); @@ -99,13 +100,13 @@ private Env getCompileEnv() { @Override - public void setParser(Parser parser) { + public void setParser(final Parser parser) { this.parser = parser; this.codeGen.setParser(parser); } - private Map getIndex2DelegateTypeMap(OperatorType opType) { + private Map getIndex2DelegateTypeMap(final OperatorType opType) { Map result = new HashMap(); switch (opType) { case AND: @@ -126,7 +127,7 @@ private Map getIndex2DelegateTypeMap(OperatorType op private int execute() { int exeCount = 0; final int size = this.tokenList.size(); - this.printTokenList(); + printTokenList(); for (int i = 0; i < size; i++) { Token token = this.tokenList.get(i); if (token.getType() == TokenType.Operator) { @@ -140,11 +141,10 @@ private int execute() { break; default: Map index2DelegateType = - this.getIndex2DelegateTypeMap(operatorType); - final int result = - this.executeOperator(i, operatorType, operandCount, index2DelegateType); + getIndex2DelegateTypeMap(operatorType); + final int result = executeOperator(i, operatorType, operandCount, index2DelegateType); if (result < 0) { - this.compactTokenList(); + compactTokenList(); return exeCount; } exeCount += result; @@ -153,13 +153,13 @@ private int execute() { } } - this.compactTokenList(); + compactTokenList(); return exeCount; } - private int executeOperator(int operatorIndex, final OperatorType operatorType, int operandCount, - Map index2DelegateType) { + private int executeOperator(final int operatorIndex, final OperatorType operatorType, + int operandCount, final Map index2DelegateType) { Token token = null; operandCount += index2DelegateType.size(); // check if literal expression can be executed @@ -176,7 +176,7 @@ private int executeOperator(int operatorIndex, final OperatorType operatorType, } final TokenType tokenType = token.getType(); // Check if operand is a literal operand - if (!this.isLiteralOperand(token, tokenType, count + 1, index2DelegateType)) { + if (!isLiteralOperand(token, tokenType, count + 1, index2DelegateType)) { canExecute = false; break; } @@ -199,22 +199,22 @@ private int executeOperator(int operatorIndex, final OperatorType operatorType, this.tokenList.set(j, null); continue; } - args[index++] = this.getAviatorObjectFromToken(token); + args[index++] = getAviatorObjectFromToken(token); // set argument token to null this.tokenList.set(j, null); } AviatorObject result = OperationRuntime.eval(getCompileEnv(), args, operatorType); // set result as token to tokenList for next executing - this.tokenList.set(operatorIndex, this.getTokenFromOperand(result)); + this.tokenList.set(operatorIndex, getTokenFromOperand(result)); return 1; } return 0; } - private boolean isLiteralOperand(Token token, final TokenType tokenType, int index, - Map index2DelegateType) { + private boolean isLiteralOperand(final Token token, final TokenType tokenType, final int index, + final Map index2DelegateType) { switch (tokenType) { case Variable: return token == Variable.TRUE || token == Variable.FALSE || token == Variable.NIL; @@ -234,7 +234,7 @@ private boolean isLiteralOperand(Token token, final TokenType tokenType, int } - private boolean isLiteralToken(Token token) { + private boolean isLiteralToken(final Token token) { switch (token.getType()) { case Variable: return token == Variable.TRUE || token == Variable.FALSE || token == Variable.NIL; @@ -254,7 +254,7 @@ private boolean isLiteralToken(Token token) { * @param operand * @return */ - private Token getTokenFromOperand(AviatorObject operand) { + private Token getTokenFromOperand(final AviatorObject operand) { Token token = null; switch (operand.getAviatorType()) { case JavaType: @@ -312,7 +312,7 @@ private void compactTokenList() { } - private AviatorObject getAviatorObjectFromToken(Token lookhead) { + private AviatorObject getAviatorObjectFromToken(final Token lookhead) { AviatorObject result = null; switch (lookhead.getType()) { case Number: @@ -349,7 +349,7 @@ private AviatorObject getAviatorObjectFromToken(Token lookhead) { @Override public Expression getResult() { // execute literal expression - while (this.execute() > 0) { + while (execute() > 0) { ; } @@ -401,12 +401,12 @@ public Expression getResult() { // Last token is a literal token,then return a LiteralExpression if (this.tokenList.size() <= 1) { if (this.tokenList.isEmpty()) { - exp = new LiteralExpression(instance, null, new ArrayList(variables.keySet())); + exp = new LiteralExpression(this.instance, null, new ArrayList(variables.keySet())); } else { final Token lastToken = this.tokenList.get(0); - if (this.isLiteralToken(lastToken)) { - exp = new LiteralExpression(instance, - this.getAviatorObjectFromToken(lastToken).getValue(null), + if (isLiteralToken(lastToken)) { + exp = new LiteralExpression(this.instance, + getAviatorObjectFromToken(lastToken).getValue(null), new ArrayList(variables.keySet())); } } @@ -414,24 +414,24 @@ public Expression getResult() { if (exp == null) { // call asm to generate byte codes - this.callASM(variables, methods); + callASM(variables, methods); // get result from asm exp = this.codeGen.getResult(); } if (exp instanceof BaseExpression) { - ((BaseExpression) exp).setCompileEnv(this.getCompileEnv()); + ((BaseExpression) exp).setCompileEnv(getCompileEnv()); } return exp; } - private void callASM(Map variables, - Map methods) { + private void callASM(final Map variables, + final Map methods) { this.codeGen.initVariables(variables); this.codeGen.initMethods(methods); - this.codeGen.setLambdaBootstraps(lambdaBootstraps); + this.codeGen.setLambdaBootstraps(this.lambdaBootstraps); this.codeGen.start(); for (int i = 0; i < this.tokenList.size(); i++) { @@ -487,7 +487,7 @@ private void callASM(Map variables, this.codeGen.onJoinRight(token); break; case FUNC: - this.codeGen.onMethodInvoke(token); + this.codeGen.onMethodInvoke(token, op.getParams()); break; case INDEX: this.codeGen.onArrayIndexEnd(token); @@ -581,7 +581,7 @@ private void printTokenList() { @Override - public void onAdd(Token lookhead) { + public void onAdd(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.ADD)); @@ -589,14 +589,14 @@ public void onAdd(Token lookhead) { @Override - public void onAndLeft(Token lookhead) { + public void onAndLeft(final Token lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.And_Left)); } @Override - public void onAndRight(Token lookhead) { + public void onAndRight(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.AND)); @@ -604,13 +604,13 @@ public void onAndRight(Token lookhead) { @Override - public void onConstant(Token lookhead) { + public void onConstant(final Token lookhead) { this.tokenList.add(lookhead); } @Override - public void onDiv(Token lookhead) { + public void onDiv(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.DIV)); @@ -618,7 +618,7 @@ public void onDiv(Token lookhead) { @Override - public void onArrayIndexStart(Token lookhead) { + public void onArrayIndexStart(final Token lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Index_Start)); @@ -627,21 +627,21 @@ public void onArrayIndexStart(Token lookhead) { @Override - public void onAssignment(Token lookhead) { + public void onAssignment(final Token lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.ASSIGNMENT)); } @Override - public void onArrayIndexEnd(Token lookhead) { + public void onArrayIndexEnd(final Token lookhead) { this.tokenList.add( new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.INDEX)); } @Override - public void onArray(Token lookhead) { + public void onArray(final Token lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Array)); @@ -649,7 +649,7 @@ public void onArray(Token lookhead) { @Override - public void onEq(Token lookhead) { + public void onEq(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.EQ)); @@ -657,7 +657,7 @@ public void onEq(Token lookhead) { @Override - public void onGe(Token lookhead) { + public void onGe(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.GE)); @@ -665,7 +665,7 @@ public void onGe(Token lookhead) { @Override - public void onGt(Token lookhead) { + public void onGt(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.GT)); @@ -673,14 +673,14 @@ public void onGt(Token lookhead) { @Override - public void onJoinLeft(Token lookhead) { + public void onJoinLeft(final Token lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Join_Left)); } @Override - public void onJoinRight(Token lookhead) { + public void onJoinRight(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.OR)); @@ -688,7 +688,7 @@ public void onJoinRight(Token lookhead) { @Override - public void onLe(Token lookhead) { + public void onLe(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.LE)); @@ -696,7 +696,7 @@ public void onLe(Token lookhead) { @Override - public void onLt(Token lookhead) { + public void onLt(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.LT)); @@ -704,7 +704,7 @@ public void onLt(Token lookhead) { @Override - public void onMatch(Token lookhead) { + public void onMatch(final Token lookhead) { this.tokenList.add( new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.MATCH)); @@ -712,15 +712,17 @@ public void onMatch(Token lookhead) { @Override - public void onMethodInvoke(Token lookhead) { - this.tokenList.add( - new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.FUNC)); + public void onMethodInvoke(final Token lookhead, final List params) { + OperatorToken token = + new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.FUNC); + token.setParams(params); + this.tokenList.add(token); } @Override - public void onMethodName(Token lookhead) { + public void onMethodName(final Token lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Method_Name)); @@ -728,7 +730,7 @@ public void onMethodName(Token lookhead) { @Override - public void onMethodParameter(Token lookhead) { + public void onMethodParameter(final Token lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Method_Param)); @@ -737,11 +739,11 @@ public void onMethodParameter(Token lookhead) { @Override - public void onLambdaDefineStart(Token lookhead) { + public void onLambdaDefineStart(final Token lookhead) { if (this.lambdaGenerator == null) { // TODO cache? this.lambdaGenerator = - new LambdaGenerator(instance, this, this.parser, this.codeGen.getClassLoader()); + new LambdaGenerator(this.instance, this, this.parser, this.codeGen.getClassLoader()); this.lambdaGenerator.setScopeInfo(this.parser.enterScope()); } else { throw new CompileExpressionErrorException("Compile lambda error"); @@ -750,24 +752,24 @@ public void onLambdaDefineStart(Token lookhead) { @Override - public void onLambdaArgument(Token lookhead) { + public void onLambdaArgument(final Token lookhead) { this.lambdaGenerator.addArgument(lookhead.getLexeme()); } @Override - public void onLambdaBodyStart(Token lookhead) { - parentCodeGenerator = this.parser.getCodeGenerator(); + public void onLambdaBodyStart(final Token lookhead) { + this.parentCodeGenerator = this.parser.getCodeGenerator(); this.parser.setCodeGenerator(this.lambdaGenerator); } @Override - public void onLambdaBodyEnd(Token lookhead) { + public void onLambdaBodyEnd(final Token lookhead) { this.lambdaGenerator.compileCallMethod(); LambdaFunctionBootstrap bootstrap = this.lambdaGenerator.getLmabdaBootstrap(); if (this.lambdaBootstraps == null) { - lambdaBootstraps = new HashMap(); + this.lambdaBootstraps = new HashMap(); } this.lambdaBootstraps.put(bootstrap.getName(), bootstrap); DelegateToken token = new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), @@ -781,7 +783,7 @@ public void onLambdaBodyEnd(Token lookhead) { @Override - public void onMod(Token lookhead) { + public void onMod(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.MOD)); @@ -789,7 +791,7 @@ public void onMod(Token lookhead) { @Override - public void onMult(Token lookhead) { + public void onMult(final Token lookhead) { this.tokenList.add( new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.MULT)); @@ -797,7 +799,7 @@ public void onMult(Token lookhead) { @Override - public void onNeg(Token lookhead) { + public void onNeg(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.NEG)); @@ -805,7 +807,7 @@ public void onNeg(Token lookhead) { @Override - public void onNeq(Token lookhead) { + public void onNeq(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.NEQ)); @@ -813,7 +815,7 @@ public void onNeq(Token lookhead) { @Override - public void onNot(Token lookhead) { + public void onNot(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.NOT)); @@ -821,7 +823,7 @@ public void onNot(Token lookhead) { @Override - public void onSub(Token lookhead) { + public void onSub(final Token lookhead) { this.tokenList .add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.SUB)); @@ -829,7 +831,7 @@ public void onSub(Token lookhead) { @Override - public void onTernaryBoolean(Token lookhead) { + public void onTernaryBoolean(final Token lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Ternary_Boolean)); @@ -837,7 +839,7 @@ public void onTernaryBoolean(Token lookhead) { @Override - public void onTernaryLeft(Token lookhead) { + public void onTernaryLeft(final Token lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Ternary_Left)); @@ -845,21 +847,21 @@ public void onTernaryLeft(Token lookhead) { @Override - public void onTernaryRight(Token lookhead) { + public void onTernaryRight(final Token lookhead) { this.tokenList.add( new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.TERNARY)); } @Override - public void onTernaryEnd(Token lookhead) { + public void onTernaryEnd(final Token lookhead) { this.tokenList.add(new DelegateToken(lookhead == null ? -1 : lookhead.getStartIndex(), lookhead, DelegateTokenType.Ternay_End)); } @Override - public void onBitAnd(Token lookhead) { + public void onBitAnd(final Token lookhead) { this.tokenList.add( new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.BIT_AND)); @@ -867,14 +869,14 @@ public void onBitAnd(Token lookhead) { @Override - public void onBitNot(Token lookhead) { + public void onBitNot(final Token lookhead) { this.tokenList.add( new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.BIT_NOT)); } @Override - public void onBitOr(Token lookhead) { + public void onBitOr(final Token lookhead) { this.tokenList.add( new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.BIT_OR)); @@ -882,7 +884,7 @@ public void onBitOr(Token lookhead) { @Override - public void onShiftLeft(Token lookhead) { + public void onShiftLeft(final Token lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.SHIFT_LEFT)); @@ -890,7 +892,7 @@ public void onShiftLeft(Token lookhead) { @Override - public void onShiftRight(Token lookhead) { + public void onShiftRight(final Token lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.SHIFT_RIGHT)); @@ -898,7 +900,7 @@ public void onShiftRight(Token lookhead) { @Override - public void onUnsignedShiftRight(Token lookhead) { + public void onUnsignedShiftRight(final Token lookhead) { this.tokenList.add(new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.U_SHIFT_RIGHT)); @@ -906,7 +908,7 @@ public void onUnsignedShiftRight(Token lookhead) { @Override - public void onBitXor(Token lookhead) { + public void onBitXor(final Token lookhead) { this.tokenList.add( new OperatorToken(lookhead == null ? -1 : lookhead.getStartIndex(), OperatorType.BIT_XOR)); 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 83f8abd3..a416b75a 100644 --- a/src/main/java/com/googlecode/aviator/code/asm/ASMCodeGenerator.java +++ b/src/main/java/com/googlecode/aviator/code/asm/ASMCodeGenerator.java @@ -72,6 +72,7 @@ import com.googlecode.aviator.lexer.token.Variable; import com.googlecode.aviator.parser.AviatorClassLoader; import com.googlecode.aviator.parser.Parser; +import com.googlecode.aviator.runtime.FunctionArgument; import com.googlecode.aviator.runtime.LambdaFunctionBootstrap; import com.googlecode.aviator.runtime.op.OperationRuntime; import com.googlecode.aviator.utils.Env; @@ -86,19 +87,20 @@ */ public class ASMCodeGenerator implements CodeGenerator { + public static final String FUNC_ARGS_INNER_VAR = "__fas__"; private static final String FIELD_PREFIX = "f"; // evaluator instance - private AviatorEvaluatorInstance instance; + private final AviatorEvaluatorInstance instance; /** * Compile environment only has the *instance*. */ - private Env compileEnv; + private final Env compileEnv; // Class Writer to generate class // private final ClassWriter clazzWriter; // Trace visitor // private ClassVisitor traceClassVisitor; // Check visitor - private ClassWriter classWriter; + private final ClassWriter classWriter; // Method visitor private MethodVisitor mv; // Class name @@ -132,6 +134,12 @@ public class ASMCodeGenerator implements CodeGenerator { private final Map> labelNameIndexMap = new IdentityHashMap>(); + /** + * function params info. + */ + private Map> funcsArgs; + + private int funcInvocationId = 0; /** * Compiled lambda functions. @@ -148,20 +156,30 @@ public class ASMCodeGenerator implements CodeGenerator { private CodeGenerator parentCodeGenerator; @Override - public void setParser(Parser parser) { + public void setParser(final Parser parser) { this.parser = parser; } - private void setMaxStacks(int newMaxStacks) { + private void setMaxStacks(final int newMaxStacks) { if (newMaxStacks > this.maxStacks) { this.maxStacks = newMaxStacks; } } + private Map> getFuncsArgs() { + if (this.funcsArgs == null) { + this.funcsArgs = new HashMap<>(); + } + return this.funcsArgs; + } - public ASMCodeGenerator(AviatorEvaluatorInstance instance, AviatorClassLoader classLoader, - OutputStream traceOut, boolean trace) { + private int getNextFuncInvocationId() { + return this.funcInvocationId++; + } + + public ASMCodeGenerator(final AviatorEvaluatorInstance instance, + final AviatorClassLoader classLoader, final OutputStream traceOut, final boolean trace) { this.classLoader = classLoader; this.instance = instance; this.compileEnv = new Env(); @@ -176,24 +194,24 @@ public ASMCodeGenerator(AviatorEvaluatorInstance instance, AviatorClassLoader cl // } else { // this.classWriter = new CheckClassAdapter(this.clazzWriter); // } - this.visitClass(); + visitClass(); } public AviatorClassLoader getClassLoader() { - return classLoader; + return this.classLoader; } LambdaGenerator getLambdaGenerator() { - return lambdaGenerator; + return this.lambdaGenerator; } public void start() { - this.makeConstructor(); - this.startVisitMethodCode(); + makeConstructor(); + startVisitMethodCode(); } @@ -207,7 +225,7 @@ private void startVisitMethodCode() { private void endVisitMethodCode() { if (this.operandsCount > 0) { - this.loadEnv(); + loadEnv(); this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "getValue", "(Ljava/util/Map;)Ljava/lang/Object;"); this.mv.visitInsn(ARETURN); @@ -285,8 +303,8 @@ private void makeConstructor() { private void visitClass() { - this.classWriter.visit(instance.getBytecodeVersion(), ACC_PUBLIC + ACC_SUPER, this.className, - null, "com/googlecode/aviator/ClassExpression", null); + this.classWriter.visit(this.instance.getBytecodeVersion(), ACC_PUBLIC + ACC_SUPER, + this.className, null, "com/googlecode/aviator/ClassExpression", null); } @@ -306,11 +324,11 @@ private Label makeLabel() { * @see com.googlecode.aviator.code.CodeGenerator#onAdd(com.googlecode.aviator .lexer.token.Token) */ @Override - public void onAdd(Token lookhead) { - this.visitBinOperator(OperatorType.ADD, "add"); + public void onAdd(final Token lookhead) { + visitBinOperator(OperatorType.ADD, "add"); } - private void loadOpType(OperatorType opType) { + private void loadOpType(final OperatorType opType) { this.pushOperand(); this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/lexer/token/OperatorType", opType.name(), "Lcom/googlecode/aviator/lexer/token/OperatorType;"); @@ -328,7 +346,7 @@ private void popOperand() { /** * Pop a operand from stack */ - private void popOperand(int n) { + private void popOperand(final int n) { this.operandsCount -= n; } @@ -339,8 +357,8 @@ private void popOperand(int n) { * @see com.googlecode.aviator.code.CodeGenerator#onSub(com.googlecode.aviator .lexer.token.Token) */ @Override - public void onSub(Token lookhead) { - this.visitBinOperator(OperatorType.SUB, "sub"); + public void onSub(final Token lookhead) { + visitBinOperator(OperatorType.SUB, "sub"); } @@ -351,14 +369,14 @@ public void onSub(Token lookhead) { * .lexer.token.Token) */ @Override - public void onMult(Token lookhead) { - this.visitBinOperator(OperatorType.MULT, "mult"); + public void onMult(final Token lookhead) { + visitBinOperator(OperatorType.MULT, "mult"); } @Override - public void onAssignment(Token lookhead) { - this.loadEnv(); + public void onAssignment(final Token lookhead) { + loadEnv(); this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorJavaType", "setValue", @@ -376,8 +394,8 @@ public void onAssignment(Token lookhead) { * @see com.googlecode.aviator.code.CodeGenerator#onDiv(com.googlecode.aviator .lexer.token.Token) */ @Override - public void onDiv(Token lookhead) { - this.visitBinOperator(OperatorType.DIV, "div"); + public void onDiv(final Token lookhead) { + visitBinOperator(OperatorType.DIV, "div"); } @@ -387,8 +405,8 @@ public void onDiv(Token lookhead) { * @see com.googlecode.aviator.code.CodeGenerator#onMod(com.googlecode.aviator .lexer.token.Token) */ @Override - public void onMod(Token lookhead) { - this.visitBinOperator(OperatorType.MOD, "mod"); + public void onMod(final Token lookhead) { + visitBinOperator(OperatorType.MOD, "mod"); } @@ -396,9 +414,9 @@ public void onMod(Token lookhead) { * Do logic operation "&&" left operand */ @Override - public void onAndLeft(Token lookhead) { - this.loadEnv(); - this.visitLeftBranch(IFEQ, OperatorType.AND); + public void onAndLeft(final Token lookhead) { + loadEnv(); + visitLeftBranch(IFEQ, OperatorType.AND); } @@ -408,7 +426,7 @@ private void visitBoolean() { } - private void pushLabel0(Label l0) { + private void pushLabel0(final Label l0) { this.l0stack.push(l0); } @@ -417,17 +435,17 @@ private void pushLabel0(Label l0) { * Do logic operation "&&" right operand */ @Override - public void onAndRight(Token lookhead) { - this.visitRightBranch(IFEQ, OperatorType.AND); + public void onAndRight(final Token lookhead) { + visitRightBranch(IFEQ, OperatorType.AND); this.popOperand(); // boolean object this.popOperand(); // environment this.pushOperand(); } - private void visitRightBranch(int ints, OperatorType opType) { + private void visitRightBranch(final int ints, final OperatorType opType) { if (!OperationRuntime.hasRuntimeContext(this.compileEnv, opType)) { - this.loadEnv(); + loadEnv(); String first = "TRUE"; String second = "FALSE"; if (opType == OperatorType.OR) { @@ -435,20 +453,20 @@ private void visitRightBranch(int ints, OperatorType opType) { second = "TRUE"; } - this.visitBoolean(); - this.mv.visitJumpInsn(ints, this.peekLabel0()); + visitBoolean(); + this.mv.visitJumpInsn(ints, peekLabel0()); // Result is true this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", first, "Lcom/googlecode/aviator/runtime/type/AviatorBoolean;"); - Label l1 = this.makeLabel(); + Label l1 = makeLabel(); this.mv.visitJumpInsn(GOTO, l1); - this.visitLabel(this.popLabel0()); + visitLabel(popLabel0()); // Result is false this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", second, "Lcom/googlecode/aviator/runtime/type/AviatorBoolean;"); - this.visitLabel(l1); + visitLabel(l1); } else { - this.loadOpType(opType); + loadOpType(opType); this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/op/OperationRuntime", "eval", "(Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;Lcom/googlecode/aviator/runtime/type/AviatorObject;Lcom/googlecode/aviator/lexer/token/OperatorType;)Lcom/googlecode/aviator/runtime/type/AviatorObject;"); @@ -464,13 +482,13 @@ private void visitRightBranch(int ints, OperatorType opType) { @Override - public void onTernaryBoolean(Token lookhead) { - this.loadEnv(); - this.visitBoolean(); - Label l0 = this.makeLabel(); - Label l1 = this.makeLabel(); - this.pushLabel0(l0); - this.pushLabel1(l1); + public void onTernaryBoolean(final Token lookhead) { + loadEnv(); + visitBoolean(); + Label l0 = makeLabel(); + Label l1 = makeLabel(); + pushLabel0(l0); + pushLabel1(l1); this.mv.visitJumpInsn(IFEQ, l0); this.popOperand(); this.popOperand(); @@ -480,15 +498,15 @@ public void onTernaryBoolean(Token lookhead) { } - private void pushLabel1(Label l1) { + private void pushLabel1(final Label l1) { this.l1stack.push(l1); } @Override - public void onTernaryLeft(Token lookhead) { - this.mv.visitJumpInsn(GOTO, this.peekLabel1()); - this.visitLabel(this.popLabel0()); + public void onTernaryLeft(final Token lookhead) { + this.mv.visitJumpInsn(GOTO, peekLabel1()); + visitLabel(popLabel0()); this.popOperand(); // pop one boolean } @@ -499,14 +517,14 @@ private Label peekLabel1() { @Override - public void onTernaryRight(Token lookhead) { - this.visitLabel(this.popLabel1()); + public void onTernaryRight(final Token lookhead) { + visitLabel(popLabel1()); this.popOperand(); // pop one boolean } @Override - public void onTernaryEnd(Token lookhead) { + public void onTernaryEnd(final Token lookhead) { while (--this.operandsCount > 0) { this.mv.visitInsn(POP); } @@ -521,8 +539,8 @@ private Label popLabel1() { * Do logic operation "||" right operand */ @Override - public void onJoinRight(Token lookhead) { - this.visitRightBranch(IFNE, OperatorType.OR); + public void onJoinRight(final Token lookhead) { + visitRightBranch(IFNE, OperatorType.OR); this.popOperand(); this.popOperand(); this.pushOperand(); @@ -530,7 +548,7 @@ public void onJoinRight(Token lookhead) { } - private void visitLabel(Label label) { + private void visitLabel(final Label label) { this.mv.visitLabel(label); this.currentLabel = label; } @@ -550,17 +568,17 @@ private Label popLabel0() { * Do logic operation "||" left operand */ @Override - public void onJoinLeft(Token lookhead) { - this.loadEnv(); - this.visitLeftBranch(IFNE, OperatorType.OR); + public void onJoinLeft(final Token lookhead) { + loadEnv(); + visitLeftBranch(IFNE, OperatorType.OR); } - private void visitLeftBranch(int ints, OperatorType opType) { + private void visitLeftBranch(final int ints, final OperatorType opType) { if (!OperationRuntime.hasRuntimeContext(this.compileEnv, opType)) { - this.visitBoolean(); - Label l0 = this.makeLabel(); - this.pushLabel0(l0); + visitBoolean(); + Label l0 = makeLabel(); + pushLabel0(l0); this.mv.visitJumpInsn(ints, l0); this.popOperand(); } @@ -569,49 +587,49 @@ private void visitLeftBranch(int ints, OperatorType opType) { @Override - public void onEq(Token lookhead) { - this.doCompareAndJump(IFNE, OperatorType.EQ); + public void onEq(final Token lookhead) { + doCompareAndJump(IFNE, OperatorType.EQ); } @Override - public void onMatch(Token lookhead) { - this.visitBinOperator(OperatorType.MATCH, "match"); + public void onMatch(final Token lookhead) { + visitBinOperator(OperatorType.MATCH, "match"); this.popOperand(); this.pushOperand(); } @Override - public void onNeq(Token lookhead) { - this.doCompareAndJump(IFEQ, OperatorType.NEQ); + public void onNeq(final Token lookhead) { + doCompareAndJump(IFEQ, OperatorType.NEQ); } - private void doCompareAndJump(int ints, OperatorType opType) { - this.loadEnv(); - this.visitCompare(ints, opType); + private void doCompareAndJump(final int ints, final OperatorType opType) { + loadEnv(); + visitCompare(ints, opType); this.popOperand(); this.popOperand(); } - private void visitCompare(int ints, OperatorType opType) { + private void visitCompare(final int ints, final OperatorType opType) { if (!OperationRuntime.hasRuntimeContext(this.compileEnv, opType)) { this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "compare", "(Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;)I"); - Label l0 = this.makeLabel(); - Label l1 = this.makeLabel(); + Label l0 = makeLabel(); + Label l1 = makeLabel(); this.mv.visitJumpInsn(ints, l0); this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "TRUE", "Lcom/googlecode/aviator/runtime/type/AviatorBoolean;"); this.mv.visitJumpInsn(GOTO, l1); - this.visitLabel(l0); + visitLabel(l0); this.mv.visitFieldInsn(GETSTATIC, "com/googlecode/aviator/runtime/type/AviatorBoolean", "FALSE", "Lcom/googlecode/aviator/runtime/type/AviatorBoolean;"); - this.visitLabel(l1); + visitLabel(l1); } else { - this.loadOpType(opType); + loadOpType(opType); this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/op/OperationRuntime", "eval", "(Lcom/googlecode/aviator/runtime/type/AviatorObject;Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;Lcom/googlecode/aviator/lexer/token/OperatorType;)Lcom/googlecode/aviator/runtime/type/AviatorObject;"); @@ -622,27 +640,27 @@ private void visitCompare(int ints, OperatorType opType) { @Override - public void onGe(Token lookhead) { - this.doCompareAndJump(IFLT, OperatorType.GE); + public void onGe(final Token lookhead) { + doCompareAndJump(IFLT, OperatorType.GE); } @Override - public void onGt(Token lookhead) { - this.doCompareAndJump(IFLE, OperatorType.GT); + public void onGt(final Token lookhead) { + doCompareAndJump(IFLE, OperatorType.GT); } @Override - public void onLe(Token lookhead) { - this.doCompareAndJump(IFGT, OperatorType.LE); + public void onLe(final Token lookhead) { + doCompareAndJump(IFGT, OperatorType.LE); } @Override - public void onLt(Token lookhead) { - this.doCompareAndJump(IFGE, OperatorType.LT); + public void onLt(final Token lookhead) { + doCompareAndJump(IFGE, OperatorType.LT); } @@ -650,10 +668,10 @@ public void onLt(Token lookhead) { * * @param extras 额外的栈空间大小 */ - public void pushOperand(int extras) { + public void pushOperand(final int extras) { this.operandsCount++; this.operandsCount += extras; - this.setMaxStacks(this.operandsCount); + setMaxStacks(this.operandsCount); } @@ -661,23 +679,23 @@ public void pushOperand(int extras) { * Logic operation '!' */ @Override - public void onNot(Token lookhead) { - this.visitUnaryOperator(OperatorType.NOT, "not"); + public void onNot(final Token lookhead) { + visitUnaryOperator(OperatorType.NOT, "not"); } - private void visitBinOperator(OperatorType opType, String methodName) { + private void visitBinOperator(final OperatorType opType, final String methodName) { if (!OperationRuntime.hasRuntimeContext(this.compileEnv, opType)) { // swap arguments for regular-expression match operator. if (opType == OperatorType.MATCH) { this.mv.visitInsn(SWAP); } - this.loadEnv(); + loadEnv(); this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", methodName, "(Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorObject;"); } else { - this.loadEnv(); - this.loadOpType(opType); + loadEnv(); + loadOpType(opType); this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/op/OperationRuntime", "eval", "(Lcom/googlecode/aviator/runtime/type/AviatorObject;Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;Lcom/googlecode/aviator/lexer/token/OperatorType;)Lcom/googlecode/aviator/runtime/type/AviatorObject;"); @@ -687,15 +705,15 @@ private void visitBinOperator(OperatorType opType, String methodName) { this.popOperand(); } - private void visitUnaryOperator(OperatorType opType, String methodName) { + private void visitUnaryOperator(final OperatorType opType, final String methodName) { this.mv.visitTypeInsn(CHECKCAST, "com/googlecode/aviator/runtime/type/AviatorObject"); - this.loadEnv(); + loadEnv(); if (!OperationRuntime.hasRuntimeContext(this.compileEnv, opType)) { this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", methodName, "(Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorObject;"); } else { - this.loadOpType(opType); + loadOpType(opType); this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/op/OperationRuntime", "eval", "(Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;Lcom/googlecode/aviator/lexer/token/OperatorType;)Lcom/googlecode/aviator/runtime/type/AviatorObject;"); @@ -711,8 +729,8 @@ private void visitUnaryOperator(OperatorType opType, String methodName) { * Bit operation '~' */ @Override - public void onBitNot(Token lookhead) { - this.visitUnaryOperator(OperatorType.BIT_NOT, "bitNot"); + public void onBitNot(final Token lookhead) { + visitUnaryOperator(OperatorType.BIT_NOT, "bitNot"); } @@ -723,8 +741,8 @@ public void onBitNot(Token lookhead) { * int) */ @Override - public void onNeg(Token lookhead) { - this.visitUnaryOperator(OperatorType.NEG, "neg"); + public void onNeg(final Token lookhead) { + visitUnaryOperator(OperatorType.NEG, "neg"); } @@ -735,7 +753,7 @@ public void onNeg(Token lookhead) { */ @Override public Expression getResult() { - this.end(); + end(); byte[] bytes = this.classWriter.toByteArray(); try { @@ -745,7 +763,8 @@ public Expression getResult() { defineClass.getConstructor(AviatorEvaluatorInstance.class, List.class); ClassExpression exp = (ClassExpression) constructor.newInstance(this.instance, new ArrayList(this.varTokens.keySet())); - exp.setLambdaBootstraps(lambdaBootstraps); + exp.setLambdaBootstraps(this.lambdaBootstraps); + exp.setFuncsArgs(this.funcsArgs); return exp; } catch (Exception e) { if (e.getCause() instanceof ExpressionRuntimeException) { @@ -756,8 +775,8 @@ public Expression getResult() { } private void end() { - this.endVisitMethodCode(); - this.endVisitClass(); + endVisitMethodCode(); + endVisitClass(); } @@ -768,7 +787,7 @@ private void end() { * .lexer.token.Token) */ @Override - public void onConstant(Token lookhead) { + public void onConstant(final Token lookhead) { if (lookhead == null) { return; } @@ -784,7 +803,7 @@ public void onConstant(Token lookhead) { this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/type/AviatorBigInt", "valueOf", "(Ljava/lang/String;)Lcom/googlecode/aviator/runtime/type/AviatorBigInt;"); } else if (TypeUtils.isDecimal(number)) { - this.loadEnv(); + loadEnv(); // this.pushOperand(); this.mv.visitLdcInsn(numberToken.getLexeme()); this.mv.visitMethodInsn(INVOKESTATIC, @@ -860,7 +879,7 @@ public void onConstant(Token lookhead) { // Variable is used more than once,store it to local if (this.varTokens.get(outterVarName) > 1) { this.mv.visitInsn(DUP); - int localIndex = this.getLocalIndex(); + int localIndex = getLocalIndex(); this.mv.visitVarInsn(ASTORE, localIndex); if (name2Index == null) { name2Index = new HashMap(); @@ -895,17 +914,17 @@ public void onConstant(Token lookhead) { } - public void setLambdaBootstraps(Map lambdaBootstraps) { + public void setLambdaBootstraps(final Map lambdaBootstraps) { this.lambdaBootstraps = lambdaBootstraps; } - public void initVariables(Map varTokens) { + public void initVariables(final Map varTokens) { this.varTokens = varTokens; this.innerVars = new HashMap(varTokens.size()); for (String outterVarName : varTokens.keySet()) { // Use inner variable name instead of outter variable name - String innerVarName = this.getInnerName(outterVarName); + String innerVarName = getInnerName(outterVarName); this.innerVars.put(outterVarName, innerVarName); this.classWriter.visitField(ACC_PRIVATE + ACC_FINAL, innerVarName, "Lcom/googlecode/aviator/runtime/type/AviatorJavaType;", null, null).visitEnd(); @@ -914,12 +933,12 @@ public void initVariables(Map varTokens) { } - public void initMethods(Map methods) { + public void initMethods(final Map methods) { this.methodTokens = methods; this.innerMethodMap = new HashMap(methods.size()); for (String outterMethodName : methods.keySet()) { // Use inner method name instead of outter method name - String innerMethodName = this.getInnerName(outterMethodName); + String innerMethodName = getInnerName(outterMethodName); this.innerMethodMap.put(outterMethodName, innerMethodName); this.classWriter.visitField(ACC_PRIVATE + ACC_FINAL, innerMethodName, "Lcom/googlecode/aviator/runtime/type/AviatorFunction;", null, null).visitEnd(); @@ -927,12 +946,12 @@ public void initMethods(Map methods) { } - private String getInnerName(String varName) { + private String getInnerName(final String varName) { return FIELD_PREFIX + this.fieldCounter++; } - private static String getInvokeMethodDesc(int paramCount) { + private static String getInvokeMethodDesc(final int paramCount) { StringBuilder sb = new StringBuilder("(Ljava/util/Map;"); if (paramCount <= 20) { for (int i = 0; i < paramCount; i++) { @@ -951,7 +970,30 @@ private static String getInvokeMethodDesc(int paramCount) { @Override - public void onMethodInvoke(Token lookhead) { + public void onMethodInvoke(final Token lookhead, final List params) { + + if (this.instance.getOptionValue(Options.CAPTURE_FUNCTION_ARGS).bool) { + if (params != null && !params.isEmpty()) { + int funcId = getNextFuncInvocationId(); + getFuncsArgs().put(funcId, params); + loadEnv(); + this.mv.visitLdcInsn(FUNC_ARGS_INNER_VAR); + this.mv.visitLdcInsn(funcId); + this.mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", + "(I)Ljava/lang/Integer;"); + this.mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + this.mv.visitInsn(POP); + this.pushOperand(); + this.pushOperand(); + this.popOperand(); + this.popOperand(); + this.popOperand(); + this.pushOperand(); + this.popOperand(); + } + } + final MethodMetaData methodMetaData = this.methodMetaDataStack.pop(); final int parameterCount = methodMetaData.parameterCount; if (parameterCount >= 20) { @@ -964,7 +1006,7 @@ public void onMethodInvoke(Token lookhead) { this.mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "size", "()I"); this.mv.visitTypeInsn(Opcodes.ANEWARRAY, "com/googlecode/aviator/runtime/type/AviatorObject"); - int arrayIndex = this.getLocalIndex(); + int arrayIndex = getLocalIndex(); this.mv.visitVarInsn(ASTORE, arrayIndex); this.mv.visitVarInsn(ALOAD, methodMetaData.variadicListIndex); this.mv.visitVarInsn(ALOAD, arrayIndex); @@ -997,7 +1039,7 @@ public void onMethodInvoke(Token lookhead) { @Override - public void onMethodParameter(Token lookhead) { + public void onMethodParameter(final Token lookhead) { MethodMetaData currentMethodMetaData = this.methodMetaDataStack.peek(); if (currentMethodMetaData.parameterCount >= 20) { // Add last param to variadic param list @@ -1018,7 +1060,7 @@ public void onMethodParameter(Token lookhead) { this.mv.visitTypeInsn(NEW, "java/util/ArrayList"); this.mv.visitInsn(DUP); this.mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "", "()V"); - int listIndex = this.getLocalIndex(); + int listIndex = getLocalIndex(); this.mv.visitVarInsn(ASTORE, listIndex); this.mv.visitVarInsn(ALOAD, listIndex); currentMethodMetaData.variadicListIndex = listIndex; @@ -1044,7 +1086,7 @@ private static class MethodMetaData { int variadicListIndex = -1; - public MethodMetaData(String methodName) { + public MethodMetaData(final String methodName) { super(); } } @@ -1053,25 +1095,25 @@ public MethodMetaData(String methodName) { @Override - public void onArray(Token lookhead) { - this.onConstant(lookhead); + public void onArray(final Token lookhead) { + onConstant(lookhead); } @Override - public void onArrayIndexStart(Token token) { - this.loadEnv(); + public void onArrayIndexStart(final Token token) { + loadEnv(); } @Override - public void onArrayIndexEnd(Token lookhead) { + public void onArrayIndexEnd(final Token lookhead) { if (!OperationRuntime.hasRuntimeContext(this.compileEnv, OperatorType.INDEX)) { this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorObject", "getElement", "(Ljava/util/Map;Lcom/googlecode/aviator/runtime/type/AviatorObject;)Lcom/googlecode/aviator/runtime/type/AviatorObject;"); } else { - this.loadOpType(OperatorType.INDEX); + loadOpType(OperatorType.INDEX); this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/op/OperationRuntime", "eval", "(Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;Lcom/googlecode/aviator/runtime/type/AviatorObject;Lcom/googlecode/aviator/lexer/token/OperatorType;)Lcom/googlecode/aviator/runtime/type/AviatorObject;"); @@ -1092,10 +1134,11 @@ public int getLocalIndex() { @Override - public void onLambdaDefineStart(Token lookhead) { + public void onLambdaDefineStart(final Token lookhead) { if (this.lambdaGenerator == null) { // TODO cache? - this.lambdaGenerator = new LambdaGenerator(instance, this, this.parser, this.classLoader); + this.lambdaGenerator = + new LambdaGenerator(this.instance, this, this.parser, this.classLoader); this.lambdaGenerator.setScopeInfo(this.parser.enterScope()); } else { throw new CompileExpressionErrorException("Compile lambda error"); @@ -1103,22 +1146,22 @@ public void onLambdaDefineStart(Token lookhead) { } @Override - public void onLambdaArgument(Token lookhead) { + public void onLambdaArgument(final Token lookhead) { this.lambdaGenerator.addArgument(lookhead.getLexeme()); } @Override - public void onLambdaBodyStart(Token lookhead) { - parentCodeGenerator = this.parser.getCodeGenerator(); + public void onLambdaBodyStart(final Token lookhead) { + this.parentCodeGenerator = this.parser.getCodeGenerator(); this.parser.setCodeGenerator(this.lambdaGenerator); } @Override - public void onLambdaBodyEnd(Token lookhead) { + public void onLambdaBodyEnd(final Token lookhead) { this.lambdaGenerator.compileCallMethod(); LambdaFunctionBootstrap bootstrap = this.lambdaGenerator.getLmabdaBootstrap(); if (this.lambdaBootstraps == null) { - lambdaBootstraps = new HashMap(); + this.lambdaBootstraps = new HashMap(); } this.lambdaBootstraps.put(bootstrap.getName(), bootstrap); genNewLambdaCode(bootstrap); @@ -1128,9 +1171,9 @@ public void onLambdaBodyEnd(Token lookhead) { } - public void genNewLambdaCode(LambdaFunctionBootstrap bootstrap) { + public void genNewLambdaCode(final LambdaFunctionBootstrap bootstrap) { this.mv.visitVarInsn(ALOAD, 0); - this.loadEnv(); + loadEnv(); this.mv.visitLdcInsn(bootstrap.getName()); this.mv.visitMethodInsn(INVOKEVIRTUAL, this.className, "newLambda", "(Lcom/googlecode/aviator/utils/Env;Ljava/lang/String;)Lcom/googlecode/aviator/runtime/function/LambdaFunction;"); @@ -1141,18 +1184,18 @@ public void genNewLambdaCode(LambdaFunctionBootstrap bootstrap) { } @Override - public void onMethodName(Token lookhead) { + public void onMethodName(final Token lookhead) { String outtterMethodName = "lambda"; if (lookhead.getType() != TokenType.Delegate) { outtterMethodName = lookhead.getLexeme(); String innerMethodName = this.innerMethodMap.get(outtterMethodName); if (innerMethodName != null) { - this.loadAviatorFunction(outtterMethodName, innerMethodName); + loadAviatorFunction(outtterMethodName, innerMethodName); } else { - this.createAviatorFunctionObject(outtterMethodName); + createAviatorFunctionObject(outtterMethodName); } } else { - this.loadEnv(); + loadEnv(); this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/RuntimeUtils", "getFunction", "(Ljava/lang/Object;Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorFunction;"); @@ -1163,12 +1206,12 @@ public void onMethodName(Token lookhead) { "wrapTrace", "(Lcom/googlecode/aviator/runtime/type/AviatorFunction;)Lcom/googlecode/aviator/runtime/type/AviatorFunction;"); } - this.loadEnv(); + loadEnv(); this.methodMetaDataStack.push(new MethodMetaData(outtterMethodName)); } - private void loadAviatorFunction(String outterMethodName, String innerMethodName) { + private void loadAviatorFunction(final String outterMethodName, final String innerMethodName) { Map name2Index = this.labelNameIndexMap.get(this.currentLabel); // Is it stored in local? if (name2Index != null && name2Index.containsKey(innerMethodName)) { @@ -1182,7 +1225,7 @@ private void loadAviatorFunction(String outterMethodName, String innerMethodName // Method is used more than once,store it to local for reusing if (this.methodTokens.get(outterMethodName) > 1) { this.mv.visitInsn(DUP); - int localIndex = this.getLocalIndex(); + int localIndex = getLocalIndex(); this.mv.visitVarInsn(ASTORE, localIndex); if (name2Index == null) { name2Index = new HashMap(); @@ -1221,8 +1264,8 @@ private void loadEnv() { } - private void createAviatorFunctionObject(String methodName) { - this.loadEnv(); + private void createAviatorFunctionObject(final String methodName) { + loadEnv(); this.pushOperand(); this.mv.visitLdcInsn(methodName); this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/RuntimeUtils", @@ -1235,40 +1278,40 @@ private void createAviatorFunctionObject(String methodName) { @Override - public void onBitAnd(Token lookhead) { - this.visitBinOperator(OperatorType.BIT_AND, "bitAnd"); + public void onBitAnd(final Token lookhead) { + visitBinOperator(OperatorType.BIT_AND, "bitAnd"); } @Override - public void onBitOr(Token lookhead) { - this.visitBinOperator(OperatorType.BIT_OR, "bitOr"); + public void onBitOr(final Token lookhead) { + visitBinOperator(OperatorType.BIT_OR, "bitOr"); } @Override - public void onBitXor(Token lookhead) { - this.visitBinOperator(OperatorType.BIT_XOR, "bitXor"); + public void onBitXor(final Token lookhead) { + visitBinOperator(OperatorType.BIT_XOR, "bitXor"); } @Override - public void onShiftLeft(Token lookhead) { - this.visitBinOperator(OperatorType.SHIFT_LEFT, "shiftLeft"); + public void onShiftLeft(final Token lookhead) { + visitBinOperator(OperatorType.SHIFT_LEFT, "shiftLeft"); } @Override - public void onShiftRight(Token lookhead) { - this.visitBinOperator(OperatorType.SHIFT_RIGHT, "shiftRight"); + public void onShiftRight(final Token lookhead) { + visitBinOperator(OperatorType.SHIFT_RIGHT, "shiftRight"); } @Override - public void onUnsignedShiftRight(Token lookhead) { - this.visitBinOperator(OperatorType.U_SHIFT_RIGHT, "unsignedShiftRight"); + public void onUnsignedShiftRight(final Token lookhead) { + visitBinOperator(OperatorType.U_SHIFT_RIGHT, "unsignedShiftRight"); } diff --git a/src/main/java/com/googlecode/aviator/lexer/ExpressionLexer.java b/src/main/java/com/googlecode/aviator/lexer/ExpressionLexer.java index 30c67688..c9af0dc3 100644 --- a/src/main/java/com/googlecode/aviator/lexer/ExpressionLexer.java +++ b/src/main/java/com/googlecode/aviator/lexer/ExpressionLexer.java @@ -47,13 +47,13 @@ public class ExpressionLexer { private final SymbolTable symbolTable; // Tokens buffer private final Stack> tokenBuffer = new Stack>(); - private AviatorEvaluatorInstance instance; - private String expression; - private MathContext mathContext; - private boolean parseFloatIntoDecimal; - private boolean parseIntegralNumberIntoDecimal; + private final AviatorEvaluatorInstance instance; + private final String expression; + private final MathContext mathContext; + private final boolean parseFloatIntoDecimal; + private final boolean parseIntegralNumberIntoDecimal; - public ExpressionLexer(AviatorEvaluatorInstance instance, String expression) { + public ExpressionLexer(final AviatorEvaluatorInstance instance, final String expression) { this.iterator = new StringCharacterIterator(expression); this.expression = expression; this.symbolTable = new SymbolTable(); @@ -72,7 +72,7 @@ public ExpressionLexer(AviatorEvaluatorInstance instance, String expression) { * * @param token */ - public void pushback(Token token) { + public void pushback(final Token token) { this.tokenBuffer.push(token); } @@ -95,7 +95,7 @@ public void prevChar() { 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f'}; - public boolean isValidHexChar(char ch) { + public boolean isValidHexChar(final char ch) { for (char c : VALID_HEX_CHAR) { if (c == ch) { return true; @@ -111,13 +111,13 @@ public int getCurrentIndex() { - public Token scan(boolean analyse) { + public Token scan(final boolean analyse) { // If buffer is not empty,return if (!this.tokenBuffer.isEmpty()) { return this.tokenBuffer.pop(); } // Skip white space or line - for (;; this.nextChar()) { + for (;; nextChar()) { if (this.peek == CharacterIterator.DONE) { return null; } @@ -135,27 +135,27 @@ public Token scan(boolean analyse) { } else { char ch = this.peek; int index = this.iterator.getIndex(); - this.nextChar(); + nextChar(); return new CharToken(ch, index); } } // if it is a hex digit if (Character.isDigit(this.peek) && this.peek == '0') { - this.nextChar(); + nextChar(); if (this.peek == 'x' || this.peek == 'X') { - this.nextChar(); + nextChar(); StringBuffer sb = new StringBuffer(); int startIndex = this.iterator.getIndex() - 2; long value = 0L; do { sb.append(this.peek); value = 16 * value + Character.digit(this.peek, 16); - this.nextChar(); - } while (this.isValidHexChar(this.peek)); + nextChar(); + } while (isValidHexChar(this.peek)); return new NumberToken(value, sb.toString(), startIndex); } else { - this.prevChar(); + prevChar(); } } @@ -183,7 +183,7 @@ public Token scan(boolean analyse) { "Illegal Number " + sb + " at " + this.iterator.getIndex()); } else { hasDot = true; - this.nextChar(); + nextChar(); } } else if (this.peek == 'N') { @@ -193,11 +193,11 @@ public Token scan(boolean analyse) { "Illegal number " + sb + " at " + this.iterator.getIndex()); } isBigInt = true; - this.nextChar(); + nextChar(); break; } else if (this.peek == 'M') { isBigDecimal = true; - this.nextChar(); + nextChar(); break; } else if (this.peek == 'e' || this.peek == 'E') { if (scientificNotation) { @@ -205,20 +205,20 @@ public Token scan(boolean analyse) { "Illegal number " + sb + " at " + this.iterator.getIndex()); } scientificNotation = true; - this.nextChar(); + nextChar(); if (this.peek == '-') { negExp = true; sb.append(this.peek); - this.nextChar(); + nextChar(); } } else { int digit = Character.digit(this.peek, 10); if (scientificNotation) { int n = digit; - this.nextChar(); + nextChar(); while (Character.isDigit(this.peek)) { n = 10 * n + Character.digit(this.peek, 10); - this.nextChar(); + nextChar(); } while (n-- > 0) { if (negExp) { @@ -231,11 +231,11 @@ public Token scan(boolean analyse) { } else if (hasDot) { dval = dval + digit / d; d = d * 10; - this.nextChar(); + nextChar(); } else { lval = 10 * lval + digit; dval = 10 * dval + digit; - this.nextChar(); + nextChar(); } } @@ -243,9 +243,9 @@ public Token scan(boolean analyse) { || this.peek == 'e' || this.peek == 'M' || this.peek == 'N'); Number value; if (isBigDecimal) { - value = new BigDecimal(this.getBigNumberLexeme(sb), this.mathContext); + value = new BigDecimal(getBigNumberLexeme(sb), this.mathContext); } else if (isBigInt) { - value = new BigInteger(this.getBigNumberLexeme(sb)); + value = new BigInteger(getBigNumberLexeme(sb)); } else if (hasDot) { if (this.parseFloatIntoDecimal && sb.length() > 1) { value = new BigDecimal(sb.toString(), this.mathContext); @@ -279,12 +279,12 @@ public Token scan(boolean analyse) { // It is a variable if (this.peek == '#') { int startIndex = this.iterator.getIndex(); - this.nextChar(); // skip $ + nextChar(); // skip $ StringBuilder sb = new StringBuilder(); while (Character.isJavaIdentifierPart(this.peek) || this.peek == '.' || this.peek == '[' || this.peek == ']') { sb.append(this.peek); - this.nextChar(); + nextChar(); } String lexeme = sb.toString(); if (lexeme.isEmpty()) { @@ -292,23 +292,23 @@ public Token scan(boolean analyse) { } Variable variable = new Variable(lexeme, startIndex); variable.setQuote(true); - return this.reserverVar(lexeme, variable); + return reserverVar(lexeme, variable); } if (Character.isJavaIdentifierStart(this.peek)) { int startIndex = this.iterator.getIndex(); StringBuilder sb = new StringBuilder(); do { sb.append(this.peek); - this.nextChar(); + nextChar(); } while (Character.isJavaIdentifierPart(this.peek) || this.peek == '.'); String lexeme = sb.toString(); Variable variable = new Variable(lexeme, startIndex); - return this.reserverVar(lexeme, variable); + return reserverVar(lexeme, variable); } if (isBinaryOP(this.peek)) { CharToken opToken = new CharToken(this.peek, this.iterator.getIndex()); - this.nextChar(); + nextChar(); return opToken; } // String @@ -320,7 +320,7 @@ public Token scan(boolean analyse) { while ((this.peek = this.iterator.next()) != left) { if (this.peek == '\\') // escape { - this.nextChar(); + nextChar(); if (this.peek == CharacterIterator.DONE) { throw new CompileExpressionErrorException( "EOF while reading string at index: " + this.iterator.getIndex()); @@ -362,12 +362,12 @@ public Token scan(boolean analyse) { sb.append(this.peek); } - this.nextChar(); + nextChar(); return new StringToken(sb.toString(), startIndex); } Token token = new CharToken(this.peek, this.iterator.getIndex()); - this.nextChar(); + nextChar(); return token; } @@ -375,7 +375,7 @@ public String getScanString() { return this.expression.substring(0, this.iterator.getIndex()); } - private Token reserverVar(String lexeme, Variable variable) { + private Token reserverVar(final String lexeme, final Variable variable) { // If it is a reserved word(true/false/nil/lambda) if (this.symbolTable.contains(lexeme)) { return this.symbolTable.getVariable(lexeme); @@ -386,7 +386,7 @@ private Token reserverVar(String lexeme, Variable variable) { } - private String getBigNumberLexeme(StringBuffer sb) { + private String getBigNumberLexeme(final StringBuffer sb) { String lexeme = sb.toString(); lexeme = lexeme.substring(0, lexeme.length() - 1); return lexeme; @@ -395,7 +395,7 @@ private String getBigNumberLexeme(StringBuffer sb) { static final char[] OPS = {'=', '>', '<', '+', '-', '*', '/', '%', '!', '&', '|'}; - public static boolean isBinaryOP(char ch) { + public static boolean isBinaryOP(final char ch) { for (char tmp : OPS) { if (tmp == ch) { return true; diff --git a/src/main/java/com/googlecode/aviator/lexer/token/OperatorToken.java b/src/main/java/com/googlecode/aviator/lexer/token/OperatorToken.java index 8a82643e..2d21a65b 100644 --- a/src/main/java/com/googlecode/aviator/lexer/token/OperatorToken.java +++ b/src/main/java/com/googlecode/aviator/lexer/token/OperatorToken.java @@ -15,26 +15,41 @@ **/ package com.googlecode.aviator.lexer.token; +import java.util.List; import java.util.Map; +import com.googlecode.aviator.runtime.FunctionArgument; /** * Operator token - * + * * @author dennis - * + * */ public class OperatorToken extends AbstractToken { private final OperatorType operatorType; + private List params; + + + + public List getParams() { + return this.params; + } + + + public void setParams(final List params) { + this.params = params; + } + public OperatorType getOperatorType() { - return operatorType; + return this.operatorType; } - public OperatorToken(int startIndex, OperatorType operatorType) { + public OperatorToken(final int startIndex, final OperatorType operatorType) { super(startIndex, operatorType.getToken()); this.operatorType = operatorType; } @@ -47,8 +62,8 @@ public com.googlecode.aviator.lexer.token.Token.TokenType getType() { @Override - public OperatorType getValue(Map env) { - return operatorType; + public OperatorType getValue(final Map env) { + return this.operatorType; } } diff --git a/src/main/java/com/googlecode/aviator/lexer/token/Variable.java b/src/main/java/com/googlecode/aviator/lexer/token/Variable.java index 48c2a943..64c0ebba 100644 --- a/src/main/java/com/googlecode/aviator/lexer/token/Variable.java +++ b/src/main/java/com/googlecode/aviator/lexer/token/Variable.java @@ -28,13 +28,14 @@ public class Variable extends AbstractToken { public static final String INSTANCE_VAR = "__instance__"; public static final String ENV_VAR = "__env__"; + public static final String FUNC_ARGS_VAR = "__args__"; public boolean isQuote() { - return quote; + return this.quote; } - public void setQuote(boolean special) { + public void setQuote(final boolean special) { this.quote = special; } @@ -46,7 +47,7 @@ public void setQuote(boolean special) { public static final Variable TRUE = new Variable("true", -1) { @Override - public Object getValue(Map env) { + public Object getValue(final Map env) { return true; } @@ -58,7 +59,7 @@ public Object getValue(Map env) { public static final Variable FALSE = new Variable("false", -1) { @Override - public Object getValue(Map env) { + public Object getValue(final Map env) { return false; } @@ -70,7 +71,7 @@ public Object getValue(Map env) { public static final Variable NIL = new Variable("nil", -1) { @Override - public Object getValue(Map env) { + public Object getValue(final Map env) { return null; } @@ -83,7 +84,7 @@ public Object getValue(Map env) { public static final Variable LAMBDA = new Variable("lambda", -1) { @Override - public Object getValue(Map env) { + public Object getValue(final Map env) { return false; } @@ -97,7 +98,7 @@ public com.googlecode.aviator.lexer.token.Token.TokenType getType() { @Override - public Object getValue(Map env) { + public Object getValue(final Map env) { if (env != null) { return env.get(this.lexeme); } else { @@ -106,7 +107,7 @@ public Object getValue(Map env) { } - public Variable(String name, int startIndex) { + public Variable(final String name, final int startIndex) { super(startIndex, name); } diff --git a/src/main/java/com/googlecode/aviator/parser/ExpressionParser.java b/src/main/java/com/googlecode/aviator/parser/ExpressionParser.java index 54793575..d188d659 100644 --- a/src/main/java/com/googlecode/aviator/parser/ExpressionParser.java +++ b/src/main/java/com/googlecode/aviator/parser/ExpressionParser.java @@ -15,11 +15,14 @@ **/ package com.googlecode.aviator.parser; +import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; +import java.util.List; import java.util.Set; import com.googlecode.aviator.AviatorEvaluatorInstance; import com.googlecode.aviator.Expression; +import com.googlecode.aviator.Options; import com.googlecode.aviator.code.CodeGenerator; import com.googlecode.aviator.exception.ExpressionSyntaxErrorException; import com.googlecode.aviator.lexer.ExpressionLexer; @@ -30,6 +33,7 @@ import com.googlecode.aviator.lexer.token.Token; import com.googlecode.aviator.lexer.token.Token.TokenType; import com.googlecode.aviator.lexer.token.Variable; +import com.googlecode.aviator.runtime.FunctionArgument; /** @@ -67,7 +71,9 @@ public class ExpressionParser implements Parser { private boolean inPattern = false; - private AviatorEvaluatorInstance instance; + private final AviatorEvaluatorInstance instance; + + private final boolean captureFuncArgs; /* @@ -77,7 +83,7 @@ public class ExpressionParser implements Parser { */ @Override public CodeGenerator getCodeGenerator() { - return codeGenerator; + return this.codeGenerator; } /* @@ -87,7 +93,7 @@ public CodeGenerator getCodeGenerator() { * CodeGenerator) */ @Override - public void setCodeGenerator(CodeGenerator codeGenerator) { + public void setCodeGenerator(final CodeGenerator codeGenerator) { this.codeGenerator = codeGenerator; } @@ -98,7 +104,8 @@ public void setCodeGenerator(CodeGenerator codeGenerator) { */ @Override public ScopeInfo enterScope() { - ScopeInfo info = new ScopeInfo(parenDepth, bracketDepth, lambdaDepth, depthState); + ScopeInfo info = + new ScopeInfo(this.parenDepth, this.bracketDepth, this.lambdaDepth, this.depthState); this.parenDepth = 0; this.bracketDepth = 0; this.lambdaDepth = 0; @@ -112,17 +119,18 @@ public ScopeInfo enterScope() { * @see com.googlecode.aviator.parser.Parser#restoreScope(com.googlecode.aviator.parser.ExpressionParser.DepthInfo) */ @Override - public void restoreScope(ScopeInfo info) { + public void restoreScope(final ScopeInfo info) { this.parenDepth = info.parenDepth; this.bracketDepth = info.bracketDepth; this.lambdaDepth = info.lambdaDepth; this.depthState = info.depthState; } - public ExpressionParser(AviatorEvaluatorInstance instance, ExpressionLexer lexer, - CodeGenerator codeGenerator) { + public ExpressionParser(final AviatorEvaluatorInstance instance, final ExpressionLexer lexer, + final CodeGenerator codeGenerator) { super(); this.instance = instance; + this.captureFuncArgs = instance.getOptionValue(Options.CAPTURE_FUNCTION_ARGS).bool; this.lexer = lexer; this.lookhead = this.lexer.scan(); if (this.lookhead == null) { @@ -134,38 +142,38 @@ public ExpressionParser(AviatorEvaluatorInstance instance, ExpressionLexer lexer public void ternary() { - this.join(); - if (this.lookhead == null || this.expectChar(':') || this.expectChar(',')) { + join(); + if (this.lookhead == null || expectChar(':') || expectChar(',')) { return; } - if (this.expectChar('?')) { - this.move(true); + if (expectChar('?')) { + move(true); this.codeGenerator.onTernaryBoolean(this.lookhead); - this.ternary(); - if (this.expectChar(':')) { - this.move(true); + ternary(); + if (expectChar(':')) { + move(true); this.codeGenerator.onTernaryLeft(this.lookhead); - this.ternary(); + ternary(); this.codeGenerator.onTernaryRight(this.lookhead); } else { - this.reportSyntaxError("expect ':'"); + reportSyntaxError("expect ':'"); } } } public void join() { - this.and(); + and(); while (true) { - if (this.isJoinToken()) { + if (isJoinToken()) { this.codeGenerator.onJoinLeft(this.lookhead); - this.move(true); - if (this.isJoinToken()) { - this.move(true); - this.and(); + move(true); + if (isJoinToken()) { + move(true); + and(); this.codeGenerator.onJoinRight(this.lookhead); } else { - this.reportSyntaxError("expect '|'"); + reportSyntaxError("expect '|'"); } } else { if (this.lookhead == null) { @@ -180,11 +188,11 @@ public void join() { private boolean isJoinToken() { - return this.expectChar('|'); + return expectChar('|'); } - private boolean expectChar(char ch) { + private boolean expectChar(final char ch) { if (this.lookhead == null) { return false; } @@ -193,20 +201,20 @@ private boolean expectChar(char ch) { private boolean isAndToken() { - return this.expectChar('&'); + return expectChar('&'); } public void bitOr() { - this.xor(); + xor(); while (true) { - if (this.isJoinToken()) { - this.move(true); - if (this.isJoinToken()) { - this.back(); + if (isJoinToken()) { + move(true); + if (isJoinToken()) { + back(); break; } - this.xor(); + xor(); this.codeGenerator.onBitOr(this.lookhead); } else { break; @@ -216,11 +224,11 @@ public void bitOr() { public void xor() { - this.bitAnd(); + bitAnd(); while (true) { - if (this.expectChar('^')) { - this.move(true); - this.bitAnd(); + if (expectChar('^')) { + move(true); + bitAnd(); this.codeGenerator.onBitXor(this.lookhead); } else { break; @@ -230,15 +238,15 @@ public void xor() { public void bitAnd() { - this.equality(); + equality(); while (true) { - if (this.isAndToken()) { - this.move(true); - if (this.isAndToken()) { - this.back(); + if (isAndToken()) { + move(true); + if (isAndToken()) { + back(); break; } - this.equality(); + equality(); this.codeGenerator.onBitAnd(this.lookhead); } else { break; @@ -248,17 +256,17 @@ public void bitAnd() { public void and() { - this.bitOr(); + bitOr(); while (true) { - if (this.isAndToken()) { + if (isAndToken()) { this.codeGenerator.onAndLeft(this.lookhead); - this.move(true); - if (this.isAndToken()) { - this.move(true); - this.bitOr(); + move(true); + if (isAndToken()) { + move(true); + bitOr(); this.codeGenerator.onAndRight(this.lookhead); } else { - this.reportSyntaxError("expect '&'"); + reportSyntaxError("expect '&'"); } } else { break; @@ -269,34 +277,34 @@ public void and() { public void equality() { - this.rel(); + rel(); while (true) { - if (this.expectChar('=')) { - this.move(true); - if (this.expectChar('=')) { - this.move(true); - this.rel(); + if (expectChar('=')) { + move(true); + if (expectChar('=')) { + move(true); + rel(); this.codeGenerator.onEq(this.lookhead); - } else if (this.expectChar('~')) { + } else if (expectChar('~')) { // It is a regular expression - this.move(true); - this.rel(); + move(true); + rel(); this.codeGenerator.onMatch(this.lookhead); } else { // this.back(); // assignment - this.ternary(); + ternary(); this.codeGenerator.onAssignment(this.lookhead); // this.reportSyntaxError("Aviator doesn't support assignment"); } - } else if (this.expectChar('!')) { - this.move(true); - if (this.expectChar('=')) { - this.move(true); - this.rel(); + } else if (expectChar('!')) { + move(true); + if (expectChar('=')) { + move(true); + rel(); this.codeGenerator.onNeq(this.lookhead); } else { - this.reportSyntaxError("expect '='"); + reportSyntaxError("expect '='"); } } else { break; @@ -306,26 +314,26 @@ public void equality() { public void rel() { - this.shift(); + shift(); while (true) { - if (this.expectChar('<')) { - this.move(true); - if (this.expectChar('=')) { - this.move(true); - this.expr(); + if (expectChar('<')) { + move(true); + if (expectChar('=')) { + move(true); + expr(); this.codeGenerator.onLe(this.lookhead); } else { - this.expr(); + expr(); this.codeGenerator.onLt(this.lookhead); } - } else if (this.expectChar('>')) { - this.move(true); - if (this.expectChar('=')) { - this.move(true); - this.expr(); + } else if (expectChar('>')) { + move(true); + if (expectChar('=')) { + move(true); + expr(); this.codeGenerator.onGe(this.lookhead); } else { - this.expr(); + expr(); this.codeGenerator.onGt(this.lookhead); } } else { @@ -336,33 +344,33 @@ public void rel() { public void shift() { - this.expr(); + expr(); while (true) { - if (this.expectChar('<')) { - this.move(true); - if (this.expectChar('<')) { - this.move(true); - this.expr(); + if (expectChar('<')) { + move(true); + if (expectChar('<')) { + move(true); + expr(); this.codeGenerator.onShiftLeft(this.lookhead); } else { - this.back(); + back(); break; } - } else if (this.expectChar('>')) { - this.move(true); - if (this.expectChar('>')) { - this.move(true); - if (this.expectChar('>')) { - this.move(true); - this.expr(); + } else if (expectChar('>')) { + move(true); + if (expectChar('>')) { + move(true); + if (expectChar('>')) { + move(true); + expr(); this.codeGenerator.onUnsignedShiftRight(this.lookhead); } else { - this.expr(); + expr(); this.codeGenerator.onShiftRight(this.lookhead); } } else { - this.back(); + back(); break; } } else { @@ -373,15 +381,15 @@ public void shift() { public void expr() { - this.term(); + term(); while (true) { - if (this.expectChar('+')) { - this.move(true); - this.term(); + if (expectChar('+')) { + move(true); + term(); this.codeGenerator.onAdd(this.lookhead); - } else if (this.expectChar('-')) { - this.move(true); - this.term(); + } else if (expectChar('-')) { + move(true); + term(); this.codeGenerator.onSub(this.lookhead); } else { break; @@ -391,19 +399,19 @@ public void expr() { public void term() { - this.unary(); + unary(); while (true) { - if (this.expectChar('*')) { - this.move(true); - this.unary(); + if (expectChar('*')) { + move(true); + unary(); this.codeGenerator.onMult(this.lookhead); - } else if (this.expectChar('/')) { - this.move(true); - this.unary(); + } else if (expectChar('/')) { + move(true); + unary(); this.codeGenerator.onDiv(this.lookhead); - } else if (this.expectChar('%')) { - this.move(true); - this.unary(); + } else if (expectChar('%')) { + move(true); + unary(); this.codeGenerator.onMod(this.lookhead); } else { break; @@ -413,40 +421,41 @@ public void term() { public void unary() { - if (this.expectChar('!')) { - this.move(true); + if (expectChar('!')) { + move(true); // check if it is a seq function call,"!" as variable - if (this.expectChar(',') || this.expectChar(')')) { - this.back(); - this.factor(); + if (expectChar(',') || expectChar(')')) { + back(); + factor(); } else { - this.unary(); + unary(); this.codeGenerator.onNot(this.lookhead); } - } else if (this.expectChar('-')) { - this.move(true); + } else if (expectChar('-')) { + move(true); // check if it is a seq function call,"!" as variable - if (this.expectChar(',') || this.expectChar(')')) { - this.back(); - this.factor(); + if (expectChar(',') || expectChar(')')) { + back(); + factor(); } else { - this.unary(); + unary(); this.codeGenerator.onNeg(this.lookhead); } - } else if (this.expectChar('~')) { - this.move(true); + } else if (expectChar('~')) { + move(true); // check if it is a seq function call,"~" as variable - if (this.expectChar(',') || this.expectChar(')')) { - this.back(); - this.factor(); + if (expectChar(',') || expectChar(')')) { + back(); + factor(); } else { - this.unary(); + unary(); this.codeGenerator.onBitNot(this.lookhead); } } else { - this.factor(); + factor(); } + // FIXME: it's a parser bug here while (expectChar('[') || expectChar('(')) { if (expectChar('[')) { // (...)[index] @@ -457,62 +466,100 @@ public void unary() { this.depthState.add(DepthState.Parent); this.codeGenerator.onMethodName(new DelegateToken(this.lookhead.getStartIndex(), this.lookhead, DelegateTokenType.Method_Name)); - this.move(true); - if (!this.expectChar(')')) { - this.ternary(); + move(true); + // FIXME: duplicated code with method() + List params = null; + if (this.captureFuncArgs) { + params = new ArrayList<>(); + } + int paramIndex = 0; + int lastTokenIndex = getLookheadStartIndex(); + if (!expectChar(')')) { + ternary(); this.codeGenerator.onMethodParameter(this.lookhead); - while (this.expectChar(',')) { - this.move(true); - this.ternary(); + if (this.captureFuncArgs) { + params.add(new FunctionArgument(paramIndex++, getParamExp(lastTokenIndex))); + } + + while (expectChar(',')) { + move(true); + lastTokenIndex = getLookheadStartIndex(); + ternary(); this.codeGenerator.onMethodParameter(this.lookhead); + if (this.captureFuncArgs) { + params.add(new FunctionArgument(paramIndex++, getParamExp(lastTokenIndex))); + } } } - if (this.expectChar(')')) { + if (expectChar(')')) { this.parenDepth--; this.depthState.removeLast(); - this.move(true); - this.codeGenerator.onMethodInvoke(this.lookhead); + move(true); + this.codeGenerator.onMethodInvoke(this.lookhead, params); } } } } + private int getLookheadStartIndex() { + // We should calculate the lookhead token's start index, because the token may be reserved by + // symbol table and it's start index is wrong. + return this.lookhead != null ? (this.lexer.getCurrentIndex() - getLookheadLexemeLength()) : -1; + } + + private int getLookheadLexemeLength() { + int len = this.lookhead.getLexeme().length(); + if (this.lookhead.getType() == TokenType.String) { + // Must include quote symbols. + len += 2; + } + return len; + } + + private String getParamExp(final int lastTokenIndex) { + if (lastTokenIndex >= 0 && getLookheadStartIndex() >= 0) { + return this.lexer.getScanString().substring(lastTokenIndex, getLookheadStartIndex()); + } else { + return null; + } + } + public static final CharToken LEFT_PAREN = new CharToken('(', -1); public static final CharToken RIGHT_PAREN = new CharToken(')', -1); - public boolean isOPVariable(Token token) { + public boolean isOPVariable(final Token token) { if (token.getType() != TokenType.Char) { return false; } CharToken charToken = (CharToken) token; - this.move(true); - if (this.expectChar(',') || this.expectChar(')')) { - this.back(); + move(true); + if (expectChar(',') || expectChar(')')) { + back(); String lexeme = String.valueOf(charToken.getCh()); if (lexeme.equals("-")) { lexeme = "-sub"; } return this.instance.containsFunction(lexeme); } else { - this.back(); + back(); return false; } } public void factor() { if (this.lookhead == null) { - this.reportSyntaxError("invalid value"); + reportSyntaxError("invalid value"); } - if (this.expectChar('(')) { + if (expectChar('(')) { this.parenDepth++; this.depthState.add(DepthState.Parent); - this.move(true); - this.ternary(); - if (this.expectChar(')')) { - this.move(true); + move(true); + ternary(); + if (expectChar(')')) { + move(true); this.parenDepth--; this.depthState.removeLast(); } @@ -520,37 +567,37 @@ public void factor() { } else if (this.lookhead.getType() == TokenType.Number || this.lookhead.getType() == TokenType.String || this.lookhead.getType() == TokenType.Variable || this.lookhead == Variable.TRUE - || this.lookhead == Variable.FALSE || this.isOPVariable(this.lookhead)) { + || this.lookhead == Variable.FALSE || isOPVariable(this.lookhead)) { if (this.lookhead.getType() == TokenType.Variable) { - this.checkVariableName(); + checkVariableName(); } // binary operation as variable for seq functions if (this.lookhead.getType() == TokenType.Char) { CharToken charToken = (CharToken) this.lookhead; if (!ExpressionLexer.isBinaryOP(charToken.getCh())) { - this.reportSyntaxError("Unexpect char '" + charToken.getCh() + "'"); + reportSyntaxError("Unexpect char '" + charToken.getCh() + "'"); } // make it as variable this.lookhead = new Variable(charToken.getLexeme(), charToken.getStartIndex()); } - this.move(true); + move(true); // function Token prev = this.prevToken; - if (prev.getType() == TokenType.Variable && this.expectChar('(')) { + if (prev.getType() == TokenType.Variable && expectChar('(')) { if (prev == Variable.LAMBDA) { - this.lambda(); + lambda(); } else { - this.method(); + method(); } } else if (prev.getType() == TokenType.Variable) { - this.arrayAccess(); + arrayAccess(); } else { this.codeGenerator.onConstant(prev); } - } else if (this.expectChar('/')) { - this.pattern(); + } else if (expectChar('/')) { + pattern(); } else { - this.reportSyntaxError("invalid value"); + reportSyntaxError("invalid value"); } } @@ -562,32 +609,32 @@ private void lambda() { this.codeGenerator.onLambdaDefineStart(this.prevToken); this.parenDepth++; this.depthState.add(DepthState.Parent); - this.move(true); - if (!this.expectChar(')')) { + move(true); + if (!expectChar(')')) { lambdaArgument(); - while (this.expectChar(',')) { - this.move(true); + while (expectChar(',')) { + move(true); lambdaArgument(); } } - if (this.expectChar(')')) { + if (expectChar(')')) { this.parenDepth--; this.depthState.removeLast(); - this.move(true); - if (this.expectChar('-')) { - this.move(true); - if (this.expectChar('>')) { - this.codeGenerator.onLambdaBodyStart(lookhead); - this.move(true); - this.statement(); + move(true); + if (expectChar('-')) { + move(true); + if (expectChar('>')) { + this.codeGenerator.onLambdaBodyStart(this.lookhead); + move(true); + statement(); if (this.lookhead != null && this.lookhead.getType() == TokenType.Variable && this.lookhead.getLexeme().equals("end")) { - this.codeGenerator.onLambdaBodyEnd(lookhead); + this.codeGenerator.onLambdaBodyEnd(this.lookhead); this.lambdaDepth--; this.depthState.removeLast(); - this.move(true); + move(true); } else { reportSyntaxError("Expect lambda 'end', but is: '" + currentTokenLexeme() + "'"); } @@ -610,13 +657,13 @@ private String currentTokenLexeme() { private void lambdaArgument() { if (this.lookhead.getType() == TokenType.Variable) { if (!isJavaIdentifier(this.lookhead.getLexeme())) { - this.reportSyntaxError("Illegal argument name: " + currentTokenLexeme() + ",index=" + reportSyntaxError("Illegal argument name: " + currentTokenLexeme() + ",index=" + this.lookhead.getStartIndex()); } this.codeGenerator.onLambdaArgument(this.lookhead); - this.move(true); + move(true); } else { - this.reportSyntaxError("Expect argument name,but is: " + currentTokenLexeme() + ",index=" + reportSyntaxError("Expect argument name,but is: " + currentTokenLexeme() + ",index=" + this.lookhead.getStartIndex()); } } @@ -625,13 +672,13 @@ private void lambdaArgument() { private void arrayAccess() { // check if it is a array index access boolean hasArray = false; - while (this.expectChar('[')) { + while (expectChar('[')) { if (!hasArray) { this.codeGenerator.onArray(this.prevToken); - this.move(true); + move(true); hasArray = true; } else { - this.move(true); + move(true); } this.codeGenerator.onArrayIndexStart(this.prevToken); array(); @@ -650,11 +697,11 @@ private void array() { this.prevToken.getLexeme() + " could not use [] operator"); } - this.ternary(); - if (this.expectChar(']')) { + ternary(); + if (expectChar(']')) { this.bracketDepth--; this.depthState.removeLast(); - this.move(true); + move(true); this.codeGenerator.onArrayIndexEnd(this.lookhead); } } @@ -665,7 +712,7 @@ private void checkVariableName() { String[] names = this.lookhead.getLexeme().split("\\."); for (String name : names) { if (!isJavaIdentifier(name)) { - this.reportSyntaxError( + reportSyntaxError( "Illegal identifier " + name + ",index=" + this.lookhead.getStartIndex()); } } @@ -673,25 +720,39 @@ private void checkVariableName() { } private void method() { - if (this.expectChar('(')) { + if (expectChar('(')) { + this.parenDepth++; this.depthState.add(DepthState.Parent); this.codeGenerator.onMethodName(this.prevToken); - this.move(true); - if (!this.expectChar(')')) { - this.ternary(); + move(true); + int paramIndex = 0; + List params = null; + if (this.captureFuncArgs) { + params = new ArrayList<>(); + } + int lastTokenIndex = getLookheadStartIndex(); + if (!expectChar(')')) { + ternary(); this.codeGenerator.onMethodParameter(this.lookhead); - while (this.expectChar(',')) { - this.move(true); - this.ternary(); + if (this.captureFuncArgs) { + params.add(new FunctionArgument(paramIndex++, getParamExp(lastTokenIndex))); + } + while (expectChar(',')) { + move(true); + lastTokenIndex = getLookheadStartIndex(); + ternary(); this.codeGenerator.onMethodParameter(this.lookhead); + if (this.captureFuncArgs) { + params.add(new FunctionArgument(paramIndex++, getParamExp(lastTokenIndex))); + } } } - if (this.expectChar(')')) { + if (expectChar(')')) { this.parenDepth--; this.depthState.removeLast(); - this.move(true); - this.codeGenerator.onMethodInvoke(this.lookhead); + move(true); + this.codeGenerator.onMethodInvoke(this.lookhead, params); } } } @@ -703,7 +764,7 @@ private void method() { * @param id string which should be checked * @return true if a valid identifier */ - public static final boolean isJavaIdentifier(String id) { + public static final boolean isJavaIdentifier(final String id) { if (id == null) { return false; } @@ -731,32 +792,32 @@ public static final boolean isJavaIdentifier(String id) { private void pattern() { // It is a pattern int startIndex = this.lookhead.getStartIndex(); - this.move(true); + move(true); this.inPattern = true; StringBuffer sb = new StringBuffer(); while (this.lookhead != null) { - while (!this.expectChar('/')) { + while (!expectChar('/')) { sb.append(this.lookhead.getLexeme()); - this.move(false); + move(false); } if (this.prevToken.getType() == TokenType.Char && ((CharToken) this.prevToken).getLexeme().equals("\\")) { sb.append("/"); - this.move(false); + move(false); continue; } this.inPattern = false; break; } if (this.inPattern) { - this.reportSyntaxError("invalid regular pattern"); + reportSyntaxError("invalid regular pattern"); } this.codeGenerator.onConstant(new PatternToken(sb.toString(), startIndex)); - this.move(true); + move(true); } - private void reportSyntaxError(String message) { + private void reportSyntaxError(final String message) { int index = this.lookhead != null && this.lookhead.getStartIndex() > 0 ? this.lookhead.getStartIndex() : this.lexer.getCurrentIndex(); @@ -767,12 +828,12 @@ private void reportSyntaxError(String message) { - public void move(boolean analyse) { + public void move(final boolean analyse) { if (this.lookhead != null) { this.prevToken = this.lookhead; this.lookhead = this.lexer.scan(analyse); } else { - this.reportSyntaxError("Illegal expression"); + reportSyntaxError("Illegal expression"); } } @@ -788,20 +849,20 @@ public Expression parse() { statement(); if (this.lookhead != null) { // The lookhead should be null, it's the end. - this.reportSyntaxError("Unexpect token '" + currentTokenLexeme() + "'"); + reportSyntaxError("Unexpect token '" + currentTokenLexeme() + "'"); } return this.codeGenerator.getResult(); } private void statement() { - this.ternary(); - this.ensureDepthState(); - while (this.expectChar(';')) { - this.codeGenerator.onTernaryEnd(lookhead); - this.move(true); - this.ternary(); - this.ensureDepthState(); + ternary(); + ensureDepthState(); + while (expectChar(';')) { + this.codeGenerator.onTernaryEnd(this.lookhead); + move(true); + ternary(); + ensureDepthState(); } } @@ -813,17 +874,17 @@ private void ensureDepthState() { switch (state) { case Parent: if (this.parenDepth > 0) { - this.reportSyntaxError("insert ')' to complete Expression"); + reportSyntaxError("insert ')' to complete Expression"); } break; case Bracket: if (this.bracketDepth > 0) { - this.reportSyntaxError("insert ']' to complete Expression"); + reportSyntaxError("insert ']' to complete Expression"); } break; case Lambda: if (this.lambdaDepth > 0) { - this.reportSyntaxError("insert 'end' to complete lambda Expression"); + reportSyntaxError("insert 'end' to complete lambda Expression"); } break; } diff --git a/src/main/java/com/googlecode/aviator/runtime/FunctionArgument.java b/src/main/java/com/googlecode/aviator/runtime/FunctionArgument.java new file mode 100644 index 00000000..05c37eea --- /dev/null +++ b/src/main/java/com/googlecode/aviator/runtime/FunctionArgument.java @@ -0,0 +1,45 @@ +package com.googlecode.aviator.runtime; + +/** + * A function parameter + * + * @author dennis(killme2008@gmail.com) + * + */ +public class FunctionArgument { + private final int index; + private final String expression; + + public FunctionArgument(final int index, final String name) { + super(); + this.index = index; + this.expression = name; + } + + /** + * Returns the parameter index in function,starts from zero. + * + * @return + */ + public int getIndex() { + return this.index; + } + + /** + * Returns the parameter expression. + * + * @return + */ + public String getExpression() { + return this.expression; + } + + @Override + public String toString() { + return "FunctionParameter [index=" + this.index + ", expression=" + this.expression + "]"; + } + + public static FunctionArgument from(final int index, final String name) { + return new FunctionArgument(index, name); + } +} diff --git a/src/main/java/com/googlecode/aviator/runtime/function/FunctionUtils.java b/src/main/java/com/googlecode/aviator/runtime/function/FunctionUtils.java index dc7dcddd..557c3234 100644 --- a/src/main/java/com/googlecode/aviator/runtime/function/FunctionUtils.java +++ b/src/main/java/com/googlecode/aviator/runtime/function/FunctionUtils.java @@ -15,8 +15,12 @@ **/ package com.googlecode.aviator.runtime.function; +import java.util.List; import java.util.Map; import com.googlecode.aviator.AviatorEvaluatorInstance; +import com.googlecode.aviator.BaseExpression; +import com.googlecode.aviator.code.asm.ASMCodeGenerator; +import com.googlecode.aviator.runtime.FunctionArgument; import com.googlecode.aviator.runtime.RuntimeUtils; import com.googlecode.aviator.runtime.type.AviatorFunction; import com.googlecode.aviator.runtime.type.AviatorJavaType; @@ -33,6 +37,27 @@ */ public class FunctionUtils { + /** + * Retrieve the invocation arguments info from env, returns null when absent. + * + * @param env + * @return + */ + @SuppressWarnings("unchecked") + public static List getFunctionArguments(final Map env) { + Map> funcParams = + (Map>) env.get(BaseExpression.FUNC_PARAMS_VAR); + if (funcParams == null) { + return null; + } + Integer refId = (Integer) env.get(ASMCodeGenerator.FUNC_ARGS_INNER_VAR); + if (refId == null) { + return null; + } + return funcParams.get(refId); + } + + /** * Get string value from env. * @@ -40,7 +65,8 @@ public class FunctionUtils { * @param env * @return */ - public static final String getStringValue(AviatorObject arg, Map env) { + public static final String getStringValue(final AviatorObject arg, + final Map env) { String result = null; final Object value = arg.getValue(env); @@ -59,7 +85,7 @@ public static final String getStringValue(AviatorObject arg, Map * @param env * @return */ - public static Object getJavaObject(AviatorObject arg, Map env) { + public static Object getJavaObject(final AviatorObject arg, final Map env) { if (arg.getAviatorType() != AviatorType.JavaType) { throw new ClassCastException(arg.desc(env) + " is not a javaType"); } @@ -80,7 +106,8 @@ public static Object getJavaObject(AviatorObject arg, Map env) { * @param arity * @return */ - public static AviatorFunction getFunction(AviatorObject arg, Map env, int arity) { + public static AviatorFunction getFunction(final AviatorObject arg, final Map env, + final int arity) { if (arg.getAviatorType() != AviatorType.JavaType && arg.getAviatorType() != AviatorType.Lambda) { throw new ClassCastException(arg.desc(env) + " is not a function"); @@ -121,7 +148,8 @@ public static AviatorFunction getFunction(AviatorObject arg, Map * @param env * @return */ - public static final Number getNumberValue(AviatorObject arg1, Map env) { + public static final Number getNumberValue(final AviatorObject arg1, + final Map env) { return (Number) arg1.getValue(env); } diff --git a/src/main/java/com/googlecode/aviator/runtime/type/AviatorFunction.java b/src/main/java/com/googlecode/aviator/runtime/type/AviatorFunction.java index 95b99a88..11a26493 100644 --- a/src/main/java/com/googlecode/aviator/runtime/type/AviatorFunction.java +++ b/src/main/java/com/googlecode/aviator/runtime/type/AviatorFunction.java @@ -20,22 +20,21 @@ /** * A aviator function,all functions must implement this interface - * + * * @author dennis - * + * */ public interface AviatorFunction { /** * Get the function name - * + * * @return */ public String getName(); - /** * call function - * + * * @param env Variable environment * @return */ diff --git a/src/main/java/com/googlecode/aviator/utils/Env.java b/src/main/java/com/googlecode/aviator/utils/Env.java index a89bd6ee..820aec15 100644 --- a/src/main/java/com/googlecode/aviator/utils/Env.java +++ b/src/main/java/com/googlecode/aviator/utils/Env.java @@ -33,6 +33,7 @@ import com.googlecode.aviator.AviatorEvaluatorInstance; import com.googlecode.aviator.exception.ExpressionRuntimeException; import com.googlecode.aviator.lexer.token.Variable; +import com.googlecode.aviator.runtime.function.FunctionUtils; /** * Expression execute environment.Modifed from ChainedMap in jibx. @@ -56,14 +57,14 @@ public class Env implements Map { private Map mOverrides; public Map getDefaults() { - return mDefaults; + return this.mDefaults; } public AviatorEvaluatorInstance getInstance() { - return instance; + return this.instance; } - public void setInstance(AviatorEvaluatorInstance instance) { + public void setInstance(final AviatorEvaluatorInstance instance) { this.instance = instance; } @@ -71,11 +72,14 @@ public void setInstance(AviatorEvaluatorInstance instance) { private Map capturedVars; - public void capture(String var, String expression) { - if (capturedVars == null) { - capturedVars = new HashMap<>(); + public void capture(final String var, final String expression) { + if (var.equals(Variable.FUNC_ARGS_VAR)) { + return; } - capturedVars.put(var, expression); + if (this.capturedVars == null) { + this.capturedVars = new HashMap<>(); + } + this.capturedVars.put(var, expression); } /** @@ -90,13 +94,13 @@ public Env() { * * @param defaults map providing defaults for keys not set directly */ - public Env(Map defaults) { - mDefaults = defaults; + public Env(final Map defaults) { + this.mDefaults = defaults; } - public Env(Map defaults, Map overrides) { - mDefaults = defaults; - mOverrides = overrides; + public Env(final Map defaults, final Map overrides) { + this.mDefaults = defaults; + this.mOverrides = overrides; } /** @@ -104,11 +108,11 @@ public Env(Map defaults, Map overrides) { */ @Override public void clear() { - if (mDefaults != EMPTY_ENV) { - mDefaults.clear(); + if (this.mDefaults != EMPTY_ENV) { + this.mDefaults.clear(); } - if (mOverrides != null && mOverrides != EMPTY_ENV) { - mOverrides.clear(); + if (this.mOverrides != null && this.mOverrides != EMPTY_ENV) { + this.mOverrides.clear(); } } @@ -121,12 +125,12 @@ public void clear() { * @return true if key defined, false if not */ @Override - public boolean containsKey(Object key) { + public boolean containsKey(final Object key) { Map overrides = getmOverrides(true); if (overrides.containsKey(key)) { return overrides.get(key) != null; } else { - return mDefaults.containsKey(key); + return this.mDefaults.containsKey(key); } } @@ -137,8 +141,8 @@ public boolean containsKey(Object key) { * @return true if value present as an override, false if not */ @Override - public boolean containsValue(Object value) { - return getmOverrides(true).containsValue(value) || mDefaults.containsValue(value); + public boolean containsValue(final Object value) { + return getmOverrides(true).containsValue(value) || this.mDefaults.containsValue(value); } /** @@ -148,8 +152,8 @@ public boolean containsValue(Object value) { */ @Override public Set> entrySet() { - Set> ret = new HashSet>(mDefaults.entrySet()); - ret.addAll(this.getmOverrides(true).entrySet()); + Set> ret = new HashSet>(this.mDefaults.entrySet()); + ret.addAll(getmOverrides(true).entrySet()); return ret; } @@ -161,17 +165,21 @@ public Set> entrySet() { * @return value (null if key not present) */ @Override - public Object get(Object key) { + public Object get(final Object key) { // Should check ENV_VAR at first + // TODO: performance tweak if (Variable.ENV_VAR.equals(key)) { return this; } + if (Variable.FUNC_ARGS_VAR.equals(key)) { + return FunctionUtils.getFunctionArguments(this); + } Map overrides = getmOverrides(true); Object ret = null; if (overrides.containsKey(key)) { ret = overrides.get(key); } else { - ret = mDefaults.get(key); + ret = this.mDefaults.get(key); } if (ret == null) { if (Variable.INSTANCE_VAR.equals(key)) { @@ -198,8 +206,8 @@ public boolean isEmpty() { */ @Override public Set keySet() { - Set ret = new HashSet(mDefaults.keySet()); - ret.addAll(this.getmOverrides(true).keySet()); + Set ret = new HashSet(this.mDefaults.keySet()); + ret.addAll(getmOverrides(true).keySet()); return ret; } @@ -211,8 +219,7 @@ public Set keySet() { * @return previous value for key (from default map, if not present in overrides) */ @Override - public Object put(String key, Object value) { - String capturedExp = null; + public Object put(final String key, final Object value) { if (this.capturedVars != null && this.capturedVars.containsKey(key)) { throw new ExpressionRuntimeException("Can't assignment value to captured variable.The `" + key + "` is already captured by lambda."); @@ -224,7 +231,7 @@ public Object put(String key, Object value) { prior = overrides.put(key, value); } else { overrides.put(key, value); - prior = mDefaults.get(key); + prior = this.mDefaults.get(key); } return prior; } @@ -235,7 +242,7 @@ public Object put(String key, Object value) { * @param map */ @Override - public void putAll(Map map) { + public void putAll(final Map map) { getmOverrides(false).putAll(map); } @@ -248,11 +255,11 @@ public void putAll(Map map) { * @return previous value for key */ @Override - public Object remove(Object key) { + public Object remove(final Object key) { if (getmOverrides(false).containsKey(key)) { return getmOverrides(false).remove(key); } else { - return mDefaults.remove(key); + return this.mDefaults.remove(key); } } @@ -274,7 +281,7 @@ public int size() { @Override public Collection values() { Collection vals = new ArrayList(); - for (String key : this.keySet()) { + for (String key : keySet()) { vals.add(get(key)); } return vals; @@ -313,13 +320,13 @@ public String toString() { return buf.toString(); } - private Map getmOverrides(boolean readOnly) { - if (mOverrides == null) { + private Map getmOverrides(final boolean readOnly) { + if (this.mOverrides == null) { if (readOnly) { return EMPTY_ENV; } - mOverrides = new HashMap(); + this.mOverrides = new HashMap(); } - return mOverrides; + return this.mOverrides; } } diff --git a/src/test/java/com/googlecode/aviator/code/asm/ASMCodeGeneratorUnitTest.java b/src/test/java/com/googlecode/aviator/code/asm/ASMCodeGeneratorUnitTest.java index cdf4df12..09d72b75 100644 --- a/src/test/java/com/googlecode/aviator/code/asm/ASMCodeGeneratorUnitTest.java +++ b/src/test/java/com/googlecode/aviator/code/asm/ASMCodeGeneratorUnitTest.java @@ -140,71 +140,71 @@ public void testOnConstant_Variable() throws Exception { @Test public void testOnAdd() throws Exception { - this.doArithOpTest(OperatorType.ADD); + doArithOpTest(OperatorType.ADD); } @Test public void testOnSub() throws Exception { - this.doArithOpTest(OperatorType.SUB); + doArithOpTest(OperatorType.SUB); } @Test public void testOnMult() throws Exception { - this.doArithOpTest(OperatorType.MULT); + doArithOpTest(OperatorType.MULT); } @Test public void testOnDiv() throws Exception { - this.doArithOpTest(OperatorType.DIV); + doArithOpTest(OperatorType.DIV); } @Test public void testOnMod() throws Exception { - this.doArithOpTest(OperatorType.MOD); + doArithOpTest(OperatorType.MOD); } @Test public void testOnBitAnd() throws Exception { - this.doArithOpTest(OperatorType.BIT_AND); + doArithOpTest(OperatorType.BIT_AND); } @Test public void testOnBitOr() throws Exception { - this.doArithOpTest(OperatorType.BIT_OR); + doArithOpTest(OperatorType.BIT_OR); } @Test public void testOnBitXor() throws Exception { - this.doArithOpTest(OperatorType.BIT_XOR); + doArithOpTest(OperatorType.BIT_XOR); } @Test public void testOnShiftLeft() throws Exception { - this.doArithOpTest(OperatorType.SHIFT_LEFT); + doArithOpTest(OperatorType.SHIFT_LEFT); } @Test public void testOnShiftRight() throws Exception { - this.doArithOpTest(OperatorType.SHIFT_RIGHT); + doArithOpTest(OperatorType.SHIFT_RIGHT); } @Test public void testOnUnsignedShiftRight() throws Exception { - this.doArithOpTest(OperatorType.U_SHIFT_RIGHT); + doArithOpTest(OperatorType.U_SHIFT_RIGHT); } - public void doArithOpTest(OperatorType operatorType) throws Exception { + public void doArithOpTest(final OperatorType operatorType) throws Exception { NumberToken a = new NumberToken(3L, "3"); NumberToken b = new NumberToken(3.5d, "3.5"); Map env = new HashMap(); @@ -216,38 +216,38 @@ public void doArithOpTest(OperatorType operatorType) throws Exception { case ADD: this.codeGenerator.onAdd(null); this.codeGenerator.onAdd(null); - Object result = this.eval(env); + Object result = eval(env); assertEquals(15.5, (Double) result, 0.001); break; case SUB: this.codeGenerator.onSub(null); this.codeGenerator.onSub(null); - result = this.eval(env); + result = eval(env); assertEquals(9.5, (Double) result, 0.001); break; case MULT: this.codeGenerator.onMult(null); this.codeGenerator.onMult(null); - result = this.eval(env); + result = eval(env); assertEquals(94.5, (Double) result, 0.001); break; case DIV: this.codeGenerator.onDiv(null); this.codeGenerator.onDiv(null); - result = this.eval(env); + result = eval(env); assertEquals(10.50, (Double) result, 0.001); break; case MOD: this.codeGenerator.onMod(null); this.codeGenerator.onMod(null); - result = this.eval(env); + result = eval(env); assertEquals(0.0, (Double) result, 0.001); break; } } - public void doBitOpTests(OperatorType operatorType) throws Exception { + public void doBitOpTests(final OperatorType operatorType) throws Exception { NumberToken a = new NumberToken(99L, "3"); NumberToken b = new NumberToken(2, "2"); Map env = new HashMap(); @@ -259,45 +259,45 @@ public void doBitOpTests(OperatorType operatorType) throws Exception { case BIT_OR: this.codeGenerator.onBitOr(null); this.codeGenerator.onBitOr(null); - Object result = this.eval(env); + Object result = eval(env); assertEquals(7 | 3 | 2, result); break; case BIT_AND: this.codeGenerator.onBitAnd(null); this.codeGenerator.onBitAnd(null); - result = this.eval(env); + result = eval(env); assertEquals(7 & 3 & 2, result); break; case BIT_XOR: this.codeGenerator.onBitXor(null); this.codeGenerator.onBitXor(null); - result = this.eval(env); + result = eval(env); assertEquals(7 ^ 3 ^ 2, result); break; case SHIFT_LEFT: this.codeGenerator.onShiftLeft(null); this.codeGenerator.onShiftLeft(null); - result = this.eval(env); + result = eval(env); assertEquals(7 << 3 << 2, result); break; case SHIFT_RIGHT: this.codeGenerator.onShiftRight(null); this.codeGenerator.onShiftRight(null); - result = this.eval(env); + result = eval(env); assertEquals(7 >> 3 >> 2, result); break; case U_SHIFT_RIGHT: this.codeGenerator.onUnsignedShiftRight(null); this.codeGenerator.onUnsignedShiftRight(null); - result = this.eval(env); + result = eval(env); assertEquals(7 >>> 3 >>> 2, result); break; } } - private Object eval(Map env) + private Object eval(final Map env) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Expression exp = this.codeGenerator.getResult(); return exp.execute(env); @@ -310,7 +310,7 @@ public void testOnAnd_False() throws Exception { this.codeGenerator.onAndLeft(null); this.codeGenerator.onConstant(Variable.FALSE); this.codeGenerator.onAndRight(null); - Object result = this.eval(new HashMap()); + Object result = eval(new HashMap()); assertEquals(Boolean.FALSE, result); } @@ -321,7 +321,7 @@ public void testOnAnd_True() throws Exception { this.codeGenerator.onAndLeft(null); this.codeGenerator.onConstant(Variable.TRUE); this.codeGenerator.onAndRight(null); - Object result = this.eval(new HashMap()); + Object result = eval(new HashMap()); assertEquals(Boolean.TRUE, result); } @@ -332,7 +332,7 @@ public void testOnJoin_True() throws Exception { this.codeGenerator.onJoinLeft(null); this.codeGenerator.onConstant(Variable.FALSE); this.codeGenerator.onJoinRight(null); - Object result = this.eval(new HashMap()); + Object result = eval(new HashMap()); assertEquals(Boolean.TRUE, result); } @@ -343,7 +343,7 @@ public void testOnJoin_False() throws Exception { this.codeGenerator.onJoinLeft(null); this.codeGenerator.onConstant(Variable.FALSE); this.codeGenerator.onJoinRight(null); - Object result = this.eval(new HashMap()); + Object result = eval(new HashMap()); assertEquals(Boolean.FALSE, result); } @@ -352,7 +352,7 @@ public void testOnJoin_False() throws Exception { public void testOnNot_True() throws Exception { this.codeGenerator.onConstant(Variable.FALSE); this.codeGenerator.onNot(null); - Object result = this.eval(new HashMap()); + Object result = eval(new HashMap()); assertEquals(Boolean.TRUE, result); } @@ -361,7 +361,7 @@ public void testOnNot_True() throws Exception { public void testOnNot_False() throws Exception { this.codeGenerator.onConstant(Variable.TRUE); this.codeGenerator.onNot(null); - Object result = this.eval(new HashMap()); + Object result = eval(new HashMap()); assertEquals(Boolean.FALSE, result); } @@ -370,7 +370,7 @@ public void testOnNot_False() throws Exception { public void testOnNeg_Long() throws Exception { this.codeGenerator.onConstant(new NumberToken(3L, "3")); this.codeGenerator.onNeg(null); - Object result = this.eval(new HashMap()); + Object result = eval(new HashMap()); assertEquals(-3L, result); } @@ -378,7 +378,7 @@ public void testOnNeg_Long() throws Exception { public void testOnBitNot1() throws Exception { this.codeGenerator.onConstant(new NumberToken(-3L, "-3")); this.codeGenerator.onBitNot(null); - Object result = this.eval(new HashMap()); + Object result = eval(new HashMap()); assertEquals(2, result); } @@ -386,7 +386,7 @@ public void testOnBitNot1() throws Exception { public void testOnBitNot2() throws Exception { this.codeGenerator.onConstant(new NumberToken(3L, "3")); this.codeGenerator.onBitNot(null); - Object result = this.eval(new HashMap()); + Object result = eval(new HashMap()); assertEquals(-4L, result); } @@ -396,48 +396,48 @@ public void testOnBitNot2() throws Exception { public void testOnNeg_Double() throws Exception { this.codeGenerator.onConstant(new NumberToken(-3.3d, "-3.3")); this.codeGenerator.onNeg(null); - Object result = this.eval(new HashMap()); + Object result = eval(new HashMap()); assertEquals(3.3, result); } @Test public void testOnEq() throws Exception { - this.doLogicOpTest(OperatorType.EQ); + doLogicOpTest(OperatorType.EQ); } @Test public void testOnNeq() throws Exception { - this.doLogicOpTest(OperatorType.NEQ); + doLogicOpTest(OperatorType.NEQ); } @Test public void testOnGt() throws Exception { - this.doLogicOpTest(OperatorType.GT); + doLogicOpTest(OperatorType.GT); } @Test public void testOnGe() throws Exception { - this.doLogicOpTest(OperatorType.GE); + doLogicOpTest(OperatorType.GE); } @Test public void testOnLt() throws Exception { - this.doLogicOpTest(OperatorType.LT); + doLogicOpTest(OperatorType.LT); } @Test public void testOnLe() throws Exception { - this.doLogicOpTest(OperatorType.LE); + doLogicOpTest(OperatorType.LE); } - public void doLogicOpTest(OperatorType operatorType) throws Exception { + public void doLogicOpTest(final OperatorType operatorType) throws Exception { NumberToken a = new NumberToken(3L, "3"); NumberToken b = new NumberToken(3L, "3"); Map env = new HashMap(); @@ -447,42 +447,42 @@ public void doLogicOpTest(OperatorType operatorType) throws Exception { this.codeGenerator.onConstant(a); this.codeGenerator.onConstant(b); this.codeGenerator.onEq(null); - Object result = this.eval(env); + Object result = eval(env); assertEquals(Boolean.TRUE, result); break; case NEQ: this.codeGenerator.onConstant(a); this.codeGenerator.onConstant(b); this.codeGenerator.onNeq(null); - result = this.eval(env); + result = eval(env); assertEquals(Boolean.FALSE, result); break; case GT: this.codeGenerator.onConstant(a); this.codeGenerator.onConstant(b); this.codeGenerator.onGt(null); - result = this.eval(env); + result = eval(env); assertEquals(Boolean.FALSE, result); break; case GE: this.codeGenerator.onConstant(a); this.codeGenerator.onConstant(b); this.codeGenerator.onGe(null); - result = this.eval(env); + result = eval(env); assertEquals(Boolean.TRUE, result); break; case LT: this.codeGenerator.onConstant(a); this.codeGenerator.onConstant(new Variable("c", 0)); this.codeGenerator.onLt(null); - result = this.eval(env); + result = eval(env); assertEquals(Boolean.TRUE, result); break; case LE: this.codeGenerator.onConstant(a); this.codeGenerator.onConstant(b); this.codeGenerator.onLe(null); - result = this.eval(env); + result = eval(env); assertEquals(Boolean.TRUE, result); break; } @@ -496,7 +496,7 @@ public void testOnMatch() throws Exception { this.codeGenerator .onConstant(new PatternToken("^[\\w\\-]([\\.\\w])+[\\w]+@([\\w\\-]+\\.)+[a-z]{2,4}$", 1)); this.codeGenerator.onMatch(null); - Object result = this.eval(new HashMap()); + Object result = eval(new HashMap()); assertEquals(Boolean.TRUE, result); } @@ -504,8 +504,8 @@ public void testOnMatch() throws Exception { @Test public void testOnMethod_withoutArguments() throws Exception { this.codeGenerator.onMethodName(new Variable("sysdate", -1)); - this.codeGenerator.onMethodInvoke(null); - Object result = this.eval(new HashMap()); + this.codeGenerator.onMethodInvoke(null, null); + Object result = eval(new HashMap()); assertNotNull(result); assertTrue(result instanceof Date); } @@ -515,18 +515,18 @@ public void testOnLambdaDefine() throws Exception { this.codeGenerator.setParser(new Parser() { @Override - public void setCodeGenerator(CodeGenerator codeGenerator) { + public void setCodeGenerator(final CodeGenerator codeGenerator) { } @Override - public void restoreScope(ScopeInfo info) { + public void restoreScope(final ScopeInfo info) { } @Override public CodeGenerator getCodeGenerator() { - return codeGenerator; + return ASMCodeGeneratorUnitTest.this.codeGenerator; } @Override @@ -538,17 +538,17 @@ public ScopeInfo enterScope() { this.codeGenerator.onLambdaDefineStart(new Variable("lambda", 0)); LambdaGenerator lambdaGenerator = this.codeGenerator.getLambdaGenerator(); assertNotNull(lambdaGenerator); - codeGenerator.onLambdaArgument(new Variable("x", 1)); - codeGenerator.onLambdaArgument(new Variable("y", 2)); - codeGenerator.onLambdaBodyStart(new Variable(">", 3)); + this.codeGenerator.onLambdaArgument(new Variable("x", 1)); + this.codeGenerator.onLambdaArgument(new Variable("y", 2)); + this.codeGenerator.onLambdaBodyStart(new Variable(">", 3)); lambdaGenerator.onConstant(new Variable("x", 4)); lambdaGenerator.onConstant(new Variable("y", 5)); lambdaGenerator.onAdd(null); - codeGenerator.onLambdaBodyEnd(new Variable("end", 7)); + this.codeGenerator.onLambdaBodyEnd(new Variable("end", 7)); HashMap env = new HashMap(); env.put("x", 2); env.put("y", 3); - Object result = this.eval(env); + Object result = eval(env); assertTrue(result instanceof LambdaFunction); assertEquals(5, ((LambdaFunction) result) .call(env, new AviatorJavaType("x"), new AviatorJavaType("y")).getValue(env)); @@ -564,8 +564,8 @@ public void testOnMethod_withTwoArguments() throws Exception { this.codeGenerator.onMethodParameter(null); this.codeGenerator.onConstant(new NumberToken(5L, "5")); this.codeGenerator.onMethodParameter(null); - this.codeGenerator.onMethodInvoke(null); - Object result = this.eval(new HashMap()); + this.codeGenerator.onMethodInvoke(null, null); + Object result = eval(new HashMap()); assertEquals("llo", result); } } diff --git a/src/test/java/com/googlecode/aviator/parser/ExpressionParserUnitTest.java b/src/test/java/com/googlecode/aviator/parser/ExpressionParserUnitTest.java index 91b59730..99d5ba3e 100644 --- a/src/test/java/com/googlecode/aviator/parser/ExpressionParserUnitTest.java +++ b/src/test/java/com/googlecode/aviator/parser/ExpressionParserUnitTest.java @@ -41,7 +41,7 @@ public void setUp() { @Test public void testIssue77() { - instance.setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true); + this.instance.setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "'一二三'=~/.*三/"), this.codeGenerator); this.parser.parse(); @@ -121,12 +121,54 @@ public void testStatement1() { assertEquals("x y + ; x y -", this.codeGenerator.getPostFixExpression()); } + + @Test + public void testParseFunctionParams() { + this.instance.setOption(Options.CAPTURE_FUNCTION_ARGS, true); + this.parser = new ExpressionParser(this.instance, + new ExpressionLexer(this.instance, "println(3+2);4"), this.codeGenerator); + this.parser.parse(); + assertEquals("3 2 + method_invoke<[FunctionParameter [index=0, expression=3+2]]> ; 4", + this.codeGenerator.getPostFixExpression()); + + { + this.codeGenerator = new FakeCodeGenerator(); + this.parser = new ExpressionParser(this.instance, + new ExpressionLexer(this.instance, "println(a,be,cdf,2+3)"), this.codeGenerator); + this.parser.parse(); + assertEquals( + "a be cdf 2 3 + method_invoke<[FunctionParameter [index=0, expression=a], FunctionParameter [index=1, expression=be], FunctionParameter [index=2, expression=cdf], FunctionParameter [index=3, expression=2+3]]>", + this.codeGenerator.getPostFixExpression()); + } + + { + this.codeGenerator = new FakeCodeGenerator(); + this.parser = new ExpressionParser(this.instance, + new ExpressionLexer(this.instance, "println(a,b,println(c,1+2))"), this.codeGenerator); + this.parser.parse(); + assertEquals( + "a b c 1 2 + method_invoke<[FunctionParameter [index=0, expression=c], FunctionParameter [index=1, expression=1+2]]> method_invoke<[FunctionParameter [index=0, expression=a], FunctionParameter [index=1, expression=b], FunctionParameter [index=2, expression=println(c,1+2)]]>", + this.codeGenerator.getPostFixExpression()); + } + + { + // function chains + this.codeGenerator = new FakeCodeGenerator(); + this.parser = new ExpressionParser(this.instance, + new ExpressionLexer(this.instance, "test(a, ab, c)(d, e)(foo)"), this.codeGenerator); + this.parser.parse(); + assertEquals( + "a ab c method_invoke<[FunctionParameter [index=0, expression=a], FunctionParameter [index=1, expression=ab], FunctionParameter [index=2, expression=c]]> d e method_invoke<[FunctionParameter [index=0, expression=d], FunctionParameter [index=1, expression=e]]> foo method_invoke<[FunctionParameter [index=0, expression=foo]]>", + this.codeGenerator.getPostFixExpression()); + } + } + @Test public void testStatement2() { this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "println(3+2);4"), this.codeGenerator); this.parser.parse(); - assertEquals("3 2 + method ; 4", this.codeGenerator.getPostFixExpression()); + assertEquals("3 2 + method_invoke<> ; 4", this.codeGenerator.getPostFixExpression()); } @Test(expected = ExpressionSyntaxErrorException.class) @@ -241,91 +283,91 @@ public void testSimpleExpression() { this.parser.parse(); assertEquals("1 3 +", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "1+3-2"), this.codeGenerator); this.parser.parse(); assertEquals("1 3 + 2 -", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "1+3-2/5"), this.codeGenerator); this.parser.parse(); assertEquals("1 3 + 2 5 / -", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "6==3"), this.codeGenerator); this.parser.parse(); assertEquals("6 3 ==", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "6>=3 && c==d.a"), this.codeGenerator); this.parser.parse(); assertEquals("6 3 >= c d.a == &&", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "6>=3 && c==d.a || 0.3<4"), this.codeGenerator); this.parser.parse(); assertEquals("6 3 >= c d.a == && 0.3 4 < ||", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "!true"), this.codeGenerator); this.parser.parse(); assertEquals("true !", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "!a && 3==1"), this.codeGenerator); this.parser.parse(); assertEquals("a ! 3 1 == &&", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "-a+2010"), this.codeGenerator); this.parser.parse(); assertEquals("a - 2010 +", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "3&2^1|4 == 5"), this.codeGenerator); this.parser.parse(); assertEquals("3 2 & 1 ^ 4 5 == |", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "3^2&3|4&~1"), this.codeGenerator); this.parser.parse(); assertEquals("3 2 3 & ^ 4 1 ~ & |", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "true || 2&1==0 ? 1 :0"), this.codeGenerator); this.parser.parse(); assertEquals("true 2 1 0 == & || 1 0 ?:", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "3+4>>1"), this.codeGenerator); this.parser.parse(); assertEquals("3 4 + 1 >>", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "3-4>>1"), this.codeGenerator); this.parser.parse(); assertEquals("3 4 - 1 >>", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "3-4<<1==0"), this.codeGenerator); this.parser.parse(); assertEquals("3 4 - 1 << 0 ==", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "3-4<<1&3"), this.codeGenerator); this.parser.parse(); @@ -346,7 +388,7 @@ public void testParseExpression_WithOneParen() { this.parser.parse(); assertEquals("3 5 2 + -", this.codeGenerator.getPostFixExpression()); - this.resetCodeGenerator(); + resetCodeGenerator(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "6>=3 && (c==d.a || 0.3<4)"), this.codeGenerator); this.parser.parse(); @@ -477,33 +519,33 @@ public void testParseExpression_WithManyParens2() { @Test public void testCommonPattern() { - this.matchPattern("^\\d+$"); - this.matchPattern("^[0-9]*[1-9][0-9]*$"); - - this.matchPattern("^((-\\d+) ?(0+))$"); - this.matchPattern("^-[0-9]*[1-9][0-9]*$"); - this.matchPattern("^-?\\d+$"); - this.matchPattern("^\\d+(\\.\\d+)?$"); - this.matchPattern( + matchPattern("^\\d+$"); + matchPattern("^[0-9]*[1-9][0-9]*$"); + + matchPattern("^((-\\d+) ?(0+))$"); + matchPattern("^-[0-9]*[1-9][0-9]*$"); + matchPattern("^-?\\d+$"); + matchPattern("^\\d+(\\.\\d+)?$"); + matchPattern( "^(([0-9]+\\.[0-9]*[1-9][0-9]*) ?([0-9]*[1-9][0-9]*\\.[0-9]+) ?([0-9]*[1-9][0-9]*))$"); - this.matchPattern("^((-\\d+(\\.\\d+)?) ?(0+(\\.0+)?))$"); - this.matchPattern( + matchPattern("^((-\\d+(\\.\\d+)?) ?(0+(\\.0+)?))$"); + matchPattern( "^(-(([0-9]+\\.[0-9]*[1-9][0-9]*) ?([0-9]*[1-9][0-9]*\\.[0-9]+) ?([0-9]*[1-9][0-9]*)))$"); - this.matchPattern("^(-?\\d+)(\\.\\d+)?$"); - this.matchPattern("^[A-Za-z]+$"); - this.matchPattern("^[A-Z]+$"); - this.matchPattern("^[a-z]+$"); - this.matchPattern("^[A-Za-z0-9]+$"); - this.matchPattern("^\\w+$"); - this.matchPattern("^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$"); - this.matchPattern("^[a-zA-z]+:\\/\\/(\\w+(-\\w+)*)(\\.(\\w+(-\\w+)*))*(\\?\\S*)?$"); - this.matchPattern("[\u4e00-\u9fa5]"); - this.matchPattern("[^\\x00-\\xff]"); - this.matchPattern("^\\x00-\\xff]"); - this.matchPattern("\\n[\\s ? ]*\\r"); - this.matchPattern("<(.*)>.*<\\/\\1>?<(.*)\\/>"); - this.matchPattern("(^\\s*)?(\\s*$)"); - this.matchPattern("[A-Z][a-z][a-z] [0-9][0-9]*, [0-9]\\{4\\}"); + matchPattern("^(-?\\d+)(\\.\\d+)?$"); + matchPattern("^[A-Za-z]+$"); + matchPattern("^[A-Z]+$"); + matchPattern("^[a-z]+$"); + matchPattern("^[A-Za-z0-9]+$"); + matchPattern("^\\w+$"); + matchPattern("^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$"); + matchPattern("^[a-zA-z]+:\\/\\/(\\w+(-\\w+)*)(\\.(\\w+(-\\w+)*))*(\\?\\S*)?$"); + matchPattern("[\u4e00-\u9fa5]"); + matchPattern("[^\\x00-\\xff]"); + matchPattern("^\\x00-\\xff]"); + matchPattern("\\n[\\s ? ]*\\r"); + matchPattern("<(.*)>.*<\\/\\1>?<(.*)\\/>"); + matchPattern("(^\\s*)?(\\s*$)"); + matchPattern("[A-Z][a-z][a-z] [0-9][0-9]*, [0-9]\\{4\\}"); } @@ -516,7 +558,7 @@ public void testComplexLogicExpression() { } - private void matchPattern(String pattern) { + private void matchPattern(final String pattern) { this.codeGenerator.reset(); this.parser = new ExpressionParser(this.instance, new ExpressionLexer(this.instance, "/" + pattern + "/"), this.codeGenerator); @@ -660,7 +702,7 @@ public void testParseFunction() { this.codeGenerator); this.parser.parse(); - assertEquals("hello fuck method", this.codeGenerator.getPostFixExpression()); + assertEquals("hello fuck method_invoke<>", this.codeGenerator.getPostFixExpression()); } @@ -671,7 +713,7 @@ public void testParseSeqFunction() { new ExpressionLexer(this.instance, "map(list,println)"), this.codeGenerator); this.parser.parse(); - assertEquals("list println method", this.codeGenerator.getPostFixExpression()); + assertEquals("list println method_invoke<>", this.codeGenerator.getPostFixExpression()); } @@ -682,7 +724,7 @@ public void testParseReduceFunction() { new ExpressionLexer(this.instance, "reduce(list,-,0)"), this.codeGenerator); this.parser.parse(); - assertEquals("list - 0 method", this.codeGenerator.getPostFixExpression()); + assertEquals("list - 0 method_invoke<>", this.codeGenerator.getPostFixExpression()); } @@ -694,7 +736,7 @@ public void testParseFunctionNested() { this.codeGenerator); this.parser.parse(); - assertEquals("hello 3 4 method hello 1 method method 3 2 > &&", + assertEquals("hello 3 4 method_invoke<> hello 1 method_invoke<> method_invoke<> 3 2 > &&", this.codeGenerator.getPostFixExpression()); } diff --git a/src/test/java/com/googlecode/aviator/parser/FakeCodeGenerator.java b/src/test/java/com/googlecode/aviator/parser/FakeCodeGenerator.java index 2ff3b886..b59fb469 100644 --- a/src/test/java/com/googlecode/aviator/parser/FakeCodeGenerator.java +++ b/src/test/java/com/googlecode/aviator/parser/FakeCodeGenerator.java @@ -15,10 +15,12 @@ **/ package com.googlecode.aviator.parser; +import java.util.List; import java.util.Stack; import com.googlecode.aviator.Expression; import com.googlecode.aviator.code.CodeGenerator; import com.googlecode.aviator.lexer.token.Token; +import com.googlecode.aviator.runtime.FunctionArgument; /** @@ -32,12 +34,12 @@ public class FakeCodeGenerator implements CodeGenerator { private boolean wasFirst = true; - private Stack scopes = new Stack(); + private final Stack scopes = new Stack(); private Parser parser; @Override - public void setParser(Parser parser) { + public void setParser(final Parser parser) { this.parser = parser; } @@ -60,18 +62,18 @@ public String getPostFixExpression() { @Override - public void onAdd(Token lookhead) { - this.appendToken("+"); + public void onAdd(final Token lookhead) { + appendToken("+"); } @Override - public void onTernaryEnd(Token lookhead) { - this.appendToken(";"); + public void onTernaryEnd(final Token lookhead) { + appendToken(";"); } - private void appendToken(String s) { + private void appendToken(final String s) { if (this.wasFirst) { this.wasFirst = false; this.sb.append(s); @@ -82,255 +84,255 @@ private void appendToken(String s) { @Override - public void onAndLeft(Token lookhead) { + public void onAndLeft(final Token lookhead) { } @Override - public void onAndRight(Token lookhead) { - this.appendToken("&&"); + public void onAndRight(final Token lookhead) { + appendToken("&&"); } @Override - public void onJoinRight(Token lookhead) { - this.appendToken("||"); + public void onJoinRight(final Token lookhead) { + appendToken("||"); } @Override - public void onTernaryBoolean(Token lookhead) { + public void onTernaryBoolean(final Token lookhead) { } @Override - public void onTernaryLeft(Token lookhead) { + public void onTernaryLeft(final Token lookhead) { } @Override - public void onTernaryRight(Token lookhead) { - this.appendToken("?:"); + public void onTernaryRight(final Token lookhead) { + appendToken("?:"); } @Override - public void onConstant(Token lookhead) { - this.appendToken(lookhead.getLexeme()); + public void onConstant(final Token lookhead) { + appendToken(lookhead.getLexeme()); } @Override - public void onDiv(Token lookhead) { - this.appendToken("/"); + public void onDiv(final Token lookhead) { + appendToken("/"); } @Override - public void onEq(Token lookhead) { - this.appendToken("=="); + public void onEq(final Token lookhead) { + appendToken("=="); } @Override - public void onAssignment(Token lookhead) { - this.appendToken("="); + public void onAssignment(final Token lookhead) { + appendToken("="); } @Override - public void onGe(Token lookhead) { - this.appendToken(">="); + public void onGe(final Token lookhead) { + appendToken(">="); } @Override - public void onGt(Token lookhead) { - this.appendToken(">"); + public void onGt(final Token lookhead) { + appendToken(">"); } @Override - public void onJoinLeft(Token lookhead) { + public void onJoinLeft(final Token lookhead) { } @Override - public void onLe(Token lookhead) { - this.appendToken("<="); + public void onLe(final Token lookhead) { + appendToken("<="); } @Override - public void onLt(Token lookhead) { - this.appendToken("<"); + public void onLt(final Token lookhead) { + appendToken("<"); } @Override - public void onMatch(Token lookhead) { - this.appendToken("=~"); + public void onMatch(final Token lookhead) { + appendToken("=~"); } @Override - public void onMod(Token lookhead) { - this.appendToken("%"); + public void onMod(final Token lookhead) { + appendToken("%"); } @Override - public void onMult(Token lookhead) { - this.appendToken("*"); + public void onMult(final Token lookhead) { + appendToken("*"); } @Override - public void onNeg(Token lookhead) { - this.appendToken("-"); + public void onNeg(final Token lookhead) { + appendToken("-"); } @Override - public void onNeq(Token lookhead) { - this.appendToken("!="); + public void onNeq(final Token lookhead) { + appendToken("!="); } @Override - public void onNot(Token lookhead) { - this.appendToken("!"); + public void onNot(final Token lookhead) { + appendToken("!"); } @Override - public void onSub(Token lookhead) { - this.appendToken("-"); + public void onSub(final Token lookhead) { + appendToken("-"); } @Override - public void onMethodInvoke(Token lookhead) { - this.appendToken("method"); + public void onMethodInvoke(final Token lookhead, final List params) { + appendToken("method_invoke<" + (params == null ? "" : params.toString()) + ">"); } @Override - public void onMethodName(Token lookhead) { + public void onMethodName(final Token lookhead) { } @Override - public void onMethodParameter(Token lookhead) { + public void onMethodParameter(final Token lookhead) { } @Override - public void onArray(Token lookhead) { - this.appendToken(lookhead.getLexeme()); + public void onArray(final Token lookhead) { + appendToken(lookhead.getLexeme()); } @Override - public void onArrayIndexStart(Token token) { + public void onArrayIndexStart(final Token token) { } @Override - public void onArrayIndexEnd(Token lookhead) { - this.appendToken("[]"); + public void onArrayIndexEnd(final Token lookhead) { + appendToken("[]"); } @Override - public void onBitAnd(Token lookhead) { - this.appendToken("&"); + public void onBitAnd(final Token lookhead) { + appendToken("&"); } @Override - public void onBitNot(Token lookhead) { - this.appendToken("~"); + public void onBitNot(final Token lookhead) { + appendToken("~"); } @Override - public void onBitOr(Token lookhead) { - this.appendToken("|"); + public void onBitOr(final Token lookhead) { + appendToken("|"); } @Override - public void onBitXor(Token lookhead) { - this.appendToken("^"); + public void onBitXor(final Token lookhead) { + appendToken("^"); } @Override - public void onShiftLeft(Token lookhead) { - this.appendToken("<<"); + public void onShiftLeft(final Token lookhead) { + appendToken("<<"); } @Override - public void onLambdaDefineStart(Token lookhead) { + public void onLambdaDefineStart(final Token lookhead) { this.scopes.push(this.parser.enterScope()); } @Override - public void onLambdaBodyStart(Token lookhead) { + public void onLambdaBodyStart(final Token lookhead) { } @Override - public void onLambdaArgument(Token lookhead) {} + public void onLambdaArgument(final Token lookhead) {} @Override - public void onLambdaBodyEnd(Token lookhead) { - this.appendToken("lambda"); + public void onLambdaBodyEnd(final Token lookhead) { + appendToken("lambda"); this.parser.restoreScope(this.scopes.pop()); } @Override - public void onShiftRight(Token lookhead) { - this.appendToken(">>"); + public void onShiftRight(final Token lookhead) { + appendToken(">>"); } @Override - public void onUnsignedShiftRight(Token lookhead) { - this.appendToken(">>>"); + public void onUnsignedShiftRight(final Token lookhead) { + appendToken(">>>"); } diff --git a/src/test/java/com/googlecode/aviator/test/function/FunctionTest.java b/src/test/java/com/googlecode/aviator/test/function/FunctionTest.java index 4f895a81..89879ca1 100644 --- a/src/test/java/com/googlecode/aviator/test/function/FunctionTest.java +++ b/src/test/java/com/googlecode/aviator/test/function/FunctionTest.java @@ -37,7 +37,12 @@ import com.googlecode.aviator.Expression; import com.googlecode.aviator.Options; import com.googlecode.aviator.exception.ExpressionRuntimeException; +import com.googlecode.aviator.runtime.FunctionArgument; import com.googlecode.aviator.runtime.RuntimeUtils; +import com.googlecode.aviator.runtime.function.AbstractFunction; +import com.googlecode.aviator.runtime.function.FunctionUtils; +import com.googlecode.aviator.runtime.type.AviatorNil; +import com.googlecode.aviator.runtime.type.AviatorObject; import com.googlecode.aviator.utils.Env; @@ -71,6 +76,77 @@ public void testArithmeticExpression() { (Double) AviatorEvaluator.execute("100%3*4.2+(37+95)/(6*3-18.0)"), 0.0001); } + @Test + public void testCaptureFunctionParams1() { + try { + AviatorEvaluator.setOption(Options.CAPTURE_FUNCTION_ARGS, true); + + List params = (List) AviatorEvaluator + .execute("f = lambda(a,bc, d) -> __args__ end; f(1,2,100+2)"); + + assertEquals(3, params.size()); + + System.out.println(params); + + assertEquals(0, params.get(0).getIndex()); + assertEquals("1", params.get(0).getExpression()); + assertEquals(1, params.get(1).getIndex()); + assertEquals("2", params.get(1).getExpression()); + assertEquals(2, params.get(2).getIndex()); + assertEquals("100+2", params.get(2).getExpression()); + } finally { + AviatorEvaluator.setOption(Options.CAPTURE_FUNCTION_ARGS, false); + } + } + + private static class CustomFunction extends AbstractFunction { + + List args; + + @Override + public String getName() { + return "myadd"; + } + + @Override + public AviatorObject call(final Map env, final AviatorObject arg1, + final AviatorObject arg2, final AviatorObject arg3, final AviatorObject arg4) { + this.args = FunctionUtils.getFunctionArguments(env); + return AviatorNil.NIL; + } + + } + + @Test + public void testCaptureFunctionParams2() { + CustomFunction function = new CustomFunction(); + try { + AviatorEvaluator.setOption(Options.CAPTURE_FUNCTION_ARGS, true); + + AviatorEvaluator.addFunction(function); + + AviatorEvaluator.execute("myadd(sum,a,'hello', 4+100)"); + + List args = function.args; + assertNotNull(args); + assertEquals(4, args.size()); + + System.out.println(args); + + assertEquals(0, args.get(0).getIndex()); + assertEquals("sum", args.get(0).getExpression()); + assertEquals(1, args.get(1).getIndex()); + assertEquals("a", args.get(1).getExpression()); + assertEquals(2, args.get(2).getIndex()); + assertEquals("'hello'", args.get(2).getExpression()); + assertEquals(3, args.get(3).getIndex()); + assertEquals("4+100", args.get(3).getExpression()); + } finally { + AviatorEvaluator.setOption(Options.CAPTURE_FUNCTION_ARGS, false); + AviatorEvaluator.removeFunction(function); + } + } + @Test public void testArithmeticExpressionWithVariable() { From db124f5f56366d4a5c83521f13f753203a9b2d58 Mon Sep 17 00:00:00 2001 From: dennis Date: Sun, 26 May 2019 15:43:43 +0800 Subject: [PATCH 5/8] (feat) Improve compile perf. --- .../aviator/code/asm/ASMCodeGenerator.java | 78 +++++++------------ 1 file changed, 27 insertions(+), 51 deletions(-) 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 a416b75a..2b5d776e 100644 --- a/src/main/java/com/googlecode/aviator/code/asm/ASMCodeGenerator.java +++ b/src/main/java/com/googlecode/aviator/code/asm/ASMCodeGenerator.java @@ -381,9 +381,7 @@ public void onAssignment(final Token lookhead) { this.mv.visitMethodInsn(INVOKEVIRTUAL, "com/googlecode/aviator/runtime/type/AviatorJavaType", "setValue", "(Lcom/googlecode/aviator/runtime/type/AviatorObject;Ljava/util/Map;)Lcom/googlecode/aviator/runtime/type/AviatorObject;"); - this.popOperand(); - this.popOperand(); - this.popOperand(); + this.popOperand(3); this.pushOperand(); } @@ -437,8 +435,7 @@ private void pushLabel0(final Label l0) { @Override public void onAndRight(final Token lookhead) { visitRightBranch(IFEQ, OperatorType.AND); - this.popOperand(); // boolean object - this.popOperand(); // environment + this.popOperand(2); // boolean object and environment this.pushOperand(); } @@ -492,7 +489,7 @@ public void onTernaryBoolean(final Token lookhead) { this.mv.visitJumpInsn(IFEQ, l0); this.popOperand(); this.popOperand(); - this.pushOperand(1); // add two booleans + this.pushOperand(2); // add two booleans this.popOperand(); // pop the last result } @@ -541,8 +538,7 @@ private Label popLabel1() { @Override public void onJoinRight(final Token lookhead) { visitRightBranch(IFNE, OperatorType.OR); - this.popOperand(); - this.popOperand(); + this.popOperand(2); this.pushOperand(); } @@ -663,14 +659,8 @@ public void onLt(final Token lookhead) { doCompareAndJump(IFGE, OperatorType.LT); } - - /** - * - * @param extras 额外的栈空间大小 - */ - public void pushOperand(final int extras) { - this.operandsCount++; - this.operandsCount += extras; + public void pushOperand(final int delta) { + this.operandsCount += delta; setMaxStacks(this.operandsCount); } @@ -830,9 +820,8 @@ public void onConstant(final Token lookhead) { this.mv.visitLdcInsn(lookhead.getValue(null)); this.mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/type/AviatorString", "", "(Ljava/lang/String;)V"); - this.pushOperand(2); - this.popOperand(); - this.popOperand(); + this.pushOperand(3); + this.popOperand(2); break; case Pattern: // load pattern @@ -841,9 +830,8 @@ public void onConstant(final Token lookhead) { this.mv.visitLdcInsn(lookhead.getValue(null)); this.mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/type/AviatorPattern", "", "(Ljava/lang/String;)V"); - this.pushOperand(2); - this.popOperand(); - this.popOperand(); + this.pushOperand(3); + this.popOperand(2); break; case Variable: // load variable @@ -886,11 +874,10 @@ public void onConstant(final Token lookhead) { this.labelNameIndexMap.put(this.currentLabel, name2Index); } name2Index.put(innerVarName, localIndex); - this.pushOperand(2); - this.popOperand(); - this.popOperand(); + this.pushOperand(3); + this.popOperand(2); } else { - this.pushOperand(1); + this.pushOperand(2); this.popOperand(); } } @@ -902,9 +889,8 @@ public void onConstant(final Token lookhead) { this.mv.visitMethodInsn(INVOKESPECIAL, "com/googlecode/aviator/runtime/type/AviatorJavaType", "", "(Ljava/lang/String;)V"); - this.pushOperand(2); - this.popOperand(); - this.popOperand(); + this.pushOperand(3); + this.popOperand(2); } } @@ -984,13 +970,10 @@ public void onMethodInvoke(final Token lookhead, final List this.mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); this.mv.visitInsn(POP); - this.pushOperand(); - this.pushOperand(); - this.popOperand(); - this.popOperand(); - this.popOperand(); - this.pushOperand(); - this.popOperand(); + this.pushOperand(2); // __args__ and ref id + this.popOperand(3); // env, __args and ref id + this.pushOperand(); // the put result + this.popOperand(); // pop the put result. } } @@ -1016,8 +999,7 @@ public void onMethodInvoke(final Token lookhead, final List this.mv.visitTypeInsn(CHECKCAST, "[Lcom/googlecode/aviator/runtime/type/AviatorObject;"); this.popOperand(); // pop list to get size - this.pushOperand(); // new array, store and load it - this.pushOperand(); // load list + this.pushOperand(2); // new array, store and load it, then load the list this.popOperand(); // list.toArray } } @@ -1047,8 +1029,7 @@ public void onMethodParameter(final Token lookhead) { this.mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z"); this.mv.visitInsn(Opcodes.POP); this.mv.visitVarInsn(ALOAD, currentMethodMetaData.variadicListIndex); - this.popOperand(); // pop list - this.popOperand(); // pop param + this.popOperand(2); // pop list and parameter this.pushOperand(); // list.add result this.popOperand(); // pop last result this.pushOperand(); // load list @@ -1078,7 +1059,7 @@ public void onMethodParameter(final Token lookhead) { private void pushOperand() { - this.pushOperand(0); + this.pushOperand(1); } private static class MethodMetaData { @@ -1120,9 +1101,7 @@ public void onArrayIndexEnd(final Token lookhead) { this.popOperand(); } - this.popOperand(); - this.popOperand(); - this.popOperand(); + this.popOperand(3); this.pushOperand(); } @@ -1177,10 +1156,8 @@ public void genNewLambdaCode(final LambdaFunctionBootstrap bootstrap) { this.mv.visitLdcInsn(bootstrap.getName()); this.mv.visitMethodInsn(INVOKEVIRTUAL, this.className, "newLambda", "(Lcom/googlecode/aviator/utils/Env;Ljava/lang/String;)Lcom/googlecode/aviator/runtime/function/LambdaFunction;"); - this.pushOperand(); - this.pushOperand(); - this.popOperand(); - this.popOperand(); + this.pushOperand(2); + this.popOperand(2); } @Override @@ -1232,7 +1209,7 @@ private void loadAviatorFunction(final String outterMethodName, final String inn this.labelNameIndexMap.put(this.currentLabel, name2Index); } name2Index.put(innerMethodName, localIndex); - this.pushOperand(1); + this.pushOperand(2); this.popOperand(); } else { this.pushOperand(); @@ -1271,8 +1248,7 @@ private void createAviatorFunctionObject(final String methodName) { this.mv.visitMethodInsn(INVOKESTATIC, "com/googlecode/aviator/runtime/RuntimeUtils", "getFunction", "(Ljava/util/Map;Ljava/lang/String;)Lcom/googlecode/aviator/runtime/type/AviatorFunction;"); - this.popOperand(); - this.popOperand(); + this.popOperand(2); this.pushOperand(); } From 265f487967c49b53523079b871a1dbbe70126db7 Mon Sep 17 00:00:00 2001 From: dennis Date: Sun, 26 May 2019 16:03:29 +0800 Subject: [PATCH 6/8] (feat) Impl seq.contains_key, #122 --- .../aviator/AviatorEvaluatorInstance.java | 140 +++++++++--------- .../aviator/runtime/RuntimeUtils.java | 6 + .../function/seq/SeqContainsKeyFunction.java | 61 ++++++++ .../function/seq/SeqIncludeFunction.java | 10 +- .../com/googlecode/aviator/utils/Env.java | 10 +- .../aviator/test/function/FunctionTest.java | 25 ++++ 6 files changed, 174 insertions(+), 78 deletions(-) create mode 100644 src/main/java/com/googlecode/aviator/runtime/function/seq/SeqContainsKeyFunction.java diff --git a/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java b/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java index 8d7f8e3e..7feb0bd9 100644 --- a/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java +++ b/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java @@ -51,6 +51,7 @@ import com.googlecode.aviator.runtime.function.seq.SeqAddFunction; import com.googlecode.aviator.runtime.function.seq.SeqCompsitePredFunFunction; import com.googlecode.aviator.runtime.function.seq.SeqCompsitePredFunFunction.LogicOp; +import com.googlecode.aviator.runtime.function.seq.SeqContainsKeyFunction; import com.googlecode.aviator.runtime.function.seq.SeqCountFunction; import com.googlecode.aviator.runtime.function.seq.SeqEveryFunction; import com.googlecode.aviator.runtime.function.seq.SeqFilterFunction; @@ -134,11 +135,11 @@ public final class AviatorEvaluatorInstance { * @since 4.0.0 * @param loader */ - public void addFunctionLoader(FunctionLoader loader) { + public void addFunctionLoader(final FunctionLoader loader) { if (this.functionLoaders == null) { - functionLoaders = new ArrayList(); + this.functionLoaders = new ArrayList(); } - functionLoaders.add(loader); + this.functionLoaders.add(loader); } /** @@ -147,7 +148,7 @@ public void addFunctionLoader(FunctionLoader loader) { * @since 4.0.0 * @param loader */ - public void removeFunctionLoader(FunctionLoader loader) { + public void removeFunctionLoader(final FunctionLoader loader) { if (this.functionLoaders == null) { return; } @@ -162,7 +163,7 @@ public void removeFunctionLoader(FunctionLoader loader) { * @param opt * @param val */ - public void setOption(Options opt, Object val) { + public void setOption(final Options opt, final Object val) { if (opt == null || val == null) { throw new IllegalArgumentException("Option and value should not be null."); } @@ -183,8 +184,8 @@ public void setOption(Options opt, Object val) { */ @Deprecated @SuppressWarnings("unchecked") - public T getOption(Options opt) { - Value val = options.get(opt); + public T getOption(final Options opt) { + Value val = this.options.get(opt); if (val == null) { val = opt.getDefaultValueObject(); } @@ -198,8 +199,8 @@ public T getOption(Options opt) { * @param opt * @return */ - public Value getOptionValue(Options opt) { - Value val = options.get(opt); + public Value getOptionValue(final Options opt) { + Value val = this.options.get(opt); assert (val != null); return val; } @@ -211,7 +212,7 @@ public Value getOptionValue(Options opt) { * @return */ public int getBytecodeVersion() { - return bytecodeVersion; + return this.bytecodeVersion; } @@ -221,7 +222,7 @@ public int getBytecodeVersion() { * @see Opcodes#V1_6 * @param bytecodeVersion */ - public void setBytecodeVersion(int bytecodeVersion) { + public void setBytecodeVersion(final int bytecodeVersion) { this.bytecodeVersion = bytecodeVersion; } @@ -246,7 +247,7 @@ public Map getOptions() { * @return */ public Map getFuncMap() { - return funcMap; + return this.funcMap; } @@ -256,7 +257,7 @@ public Map getFuncMap() { * @return */ public Map getOpsMap() { - return opsMap; + return this.opsMap; } @@ -266,7 +267,7 @@ public Map getOpsMap() { * @return */ public OutputStream getTraceOutputStream() { - return traceOutputStream; + return this.traceOutputStream; } @@ -275,19 +276,20 @@ public OutputStream getTraceOutputStream() { * * @param traceOutputStream */ - public void setTraceOutputStream(OutputStream traceOutputStream) { + public void setTraceOutputStream(final OutputStream traceOutputStream) { this.traceOutputStream = traceOutputStream; } { - aviatorClassLoader = AccessController.doPrivileged(new PrivilegedAction() { + this.aviatorClassLoader = + AccessController.doPrivileged(new PrivilegedAction() { - @Override - public AviatorClassLoader run() { - return new AviatorClassLoader(AviatorEvaluatorInstance.class.getClassLoader()); - } + @Override + public AviatorClassLoader run() { + return new AviatorClassLoader(AviatorEvaluatorInstance.class.getClassLoader()); + } - }); + }); } private final Map funcMap = new HashMap(); @@ -363,6 +365,7 @@ private void loadLib() { addFunction(new SeqFilterFunction()); addFunction(new SeqSortFunction()); addFunction(new SeqIncludeFunction()); + addFunction(new SeqContainsKeyFunction()); addFunction(new SeqCountFunction()); addFunction(new SeqEveryFunction()); addFunction(new SeqNotAnyFunction()); @@ -398,10 +401,10 @@ private void loadLib() { * Create a aviator evaluator instance. */ AviatorEvaluatorInstance() { - this.loadLib(); - this.addFunctionLoader(ClassPathConfigFunctionLoader.getInstance()); + loadLib(); + addFunctionLoader(ClassPathConfigFunctionLoader.getInstance()); for (Options opt : Options.values()) { - options.put(opt, opt.getDefaultValueObject()); + this.options.put(opt, opt.getDefaultValueObject()); } } @@ -409,7 +412,7 @@ private void loadLib() { * Clear all cached compiled expression */ public void clearExpressionCache() { - cacheExpressions.clear(); + this.cacheExpressions.clear(); } @@ -428,9 +431,9 @@ public AviatorClassLoader getAviatorClassLoader() { * * @return */ - public AviatorClassLoader getAviatorClassLoader(boolean cached) { + public AviatorClassLoader getAviatorClassLoader(final boolean cached) { if (cached) { - return aviatorClassLoader; + return this.aviatorClassLoader; } else { return new AviatorClassLoader(Thread.currentThread().getContextClassLoader()); } @@ -442,7 +445,7 @@ public AviatorClassLoader getAviatorClassLoader(boolean cached) { * * @param function */ - public void addFunction(AviatorFunction function) { + public void addFunction(final AviatorFunction function) { final String name = function.getName(); addFunction(name, function); } @@ -453,15 +456,15 @@ public void addFunction(AviatorFunction function) { * @param name * @param function */ - public void addFunction(final String name, AviatorFunction function) { + public void addFunction(final String name, final AviatorFunction function) { if ("lambda".equals(name)) { throw new IllegalArgumentException("Invalid function name, lambda is a keyword."); } - if (funcMap.containsKey(name)) { + if (this.funcMap.containsKey(name)) { System.out.println("[Aviator WARN] The function '" + name + "' is already exists, but is replaced with new one."); } - funcMap.put(name, function); + this.funcMap.put(name, function); } /** @@ -471,7 +474,7 @@ public void addFunction(final String name, AviatorFunction function) { * @param expression the expression to be executed and it's result must be a function. * @since 4.0.0 */ - public void defineFunction(String name, String expression) { + public void defineFunction(final String name, final String expression) { this.defineFunction(name, expression, null); } @@ -483,7 +486,8 @@ public void defineFunction(String name, String expression) { * @param env the expression execution env * @since 4.0.0 */ - public void defineFunction(String name, String expression, Map env) { + public void defineFunction(final String name, final String expression, + final Map env) { AviatorFunction function = (AviatorFunction) this.execute(expression, env); this.addFunction(name, function); } @@ -495,8 +499,8 @@ public void defineFunction(String name, String expression, Map e * @param name * @return */ - public AviatorFunction removeFunction(String name) { - return (AviatorFunction) funcMap.remove(name); + public AviatorFunction removeFunction(final String name) { + return (AviatorFunction) this.funcMap.remove(name); } @@ -507,9 +511,9 @@ public AviatorFunction removeFunction(String name) { * @return */ public AviatorFunction getFunction(final String name) { - AviatorFunction function = (AviatorFunction) funcMap.get(name); - if (function == null && functionLoaders != null) { - for (FunctionLoader loader : functionLoaders) { + AviatorFunction function = (AviatorFunction) this.funcMap.get(name); + if (function == null && this.functionLoaders != null) { + for (FunctionLoader loader : this.functionLoaders) { if (loader != null) { function = loader.onFunctionNotFound(name); } @@ -530,8 +534,8 @@ public AviatorFunction getFunction(final String name) { * * @param function */ - public void addOpFunction(OperatorType opType, AviatorFunction function) { - opsMap.put(opType, function); + public void addOpFunction(final OperatorType opType, final AviatorFunction function) { + this.opsMap.put(opType, function); } @@ -543,8 +547,8 @@ public void addOpFunction(OperatorType opType, AviatorFunction function) { * @param opType * @return */ - public AviatorFunction getOpFunction(OperatorType opType) { - return opsMap.get(opType); + public AviatorFunction getOpFunction(final OperatorType opType) { + return this.opsMap.get(opType); } /** @@ -554,8 +558,8 @@ public AviatorFunction getOpFunction(OperatorType opType) { * @param opType * @return */ - public AviatorFunction removeOpFunction(OperatorType opType) { - return opsMap.remove(opType); + public AviatorFunction removeOpFunction(final OperatorType opType) { + return this.opsMap.remove(opType); } @@ -565,8 +569,8 @@ public AviatorFunction removeOpFunction(OperatorType opType) { * @param name * @return */ - public boolean containsFunction(String name) { - return funcMap.containsKey(name); + public boolean containsFunction(final String name) { + return this.funcMap.containsKey(name); } /** @@ -575,7 +579,7 @@ public boolean containsFunction(String name) { * @param function * @return */ - public AviatorFunction removeFunction(AviatorFunction function) { + public AviatorFunction removeFunction(final AviatorFunction function) { return removeFunction(function.getName()); } @@ -586,8 +590,8 @@ public AviatorFunction removeFunction(AviatorFunction function) { * @param expression * @return */ - public Expression getCachedExpression(String expression) { - FutureTask task = cacheExpressions.get(expression); + public Expression getCachedExpression(final String expression) { + FutureTask task = this.cacheExpressions.get(expression); if (task != null) { return getCompiledExpression(expression, task); } else { @@ -602,8 +606,8 @@ public Expression getCachedExpression(String expression) { * @return * @since 4.0.0 */ - public boolean isExpressionCached(String expression) { - return this.getCachedExpression(expression) != null; + public boolean isExpressionCached(final String expression) { + return getCachedExpression(expression) != null; } /** @@ -631,7 +635,7 @@ public Expression compile(final String expression, final boolean cached) { } if (cached) { - FutureTask task = cacheExpressions.get(expression); + FutureTask task = this.cacheExpressions.get(expression); if (task != null) { return getCompiledExpression(expression, task); } @@ -642,7 +646,7 @@ public Expression call() throws Exception { } }); - FutureTask existedTask = cacheExpressions.putIfAbsent(expression, task); + FutureTask existedTask = this.cacheExpressions.putIfAbsent(expression, task); if (existedTask == null) { existedTask = task; existedTask.run(); @@ -656,17 +660,18 @@ public Expression call() throws Exception { } - private Expression getCompiledExpression(final String expression, FutureTask task) { + private Expression getCompiledExpression(final String expression, + final FutureTask task) { try { return task.get(); } catch (Exception e) { - cacheExpressions.remove(expression); + this.cacheExpressions.remove(expression); throw new CompileExpressionErrorException("Compile expression failure:" + expression, e); } } - private Expression innerCompile(final String expression, boolean cached) { + private Expression innerCompile(final String expression, final boolean cached) { ExpressionLexer lexer = new ExpressionLexer(this, expression); CodeGenerator codeGenerator = newCodeGenerator(cached); ExpressionParser parser = new ExpressionParser(this, lexer, codeGenerator); @@ -682,21 +687,21 @@ private int getOptimizeLevel() { } - public CodeGenerator newCodeGenerator(boolean cached) { + public CodeGenerator newCodeGenerator(final boolean cached) { AviatorClassLoader classLoader = getAviatorClassLoader(cached); return newCodeGenerator(classLoader); } - public CodeGenerator newCodeGenerator(AviatorClassLoader classLoader) { + public CodeGenerator newCodeGenerator(final AviatorClassLoader classLoader) { switch (getOptimizeLevel()) { case AviatorEvaluator.COMPILE: ASMCodeGenerator asmCodeGenerator = new ASMCodeGenerator(this, classLoader, - traceOutputStream, getOptionValue(Options.TRACE).bool); + this.traceOutputStream, getOptionValue(Options.TRACE).bool); asmCodeGenerator.start(); return asmCodeGenerator; case AviatorEvaluator.EVAL: - return new OptimizeCodeGenerator(this, classLoader, traceOutputStream, + return new OptimizeCodeGenerator(this, classLoader, this.traceOutputStream, getOptionValue(Options.TRACE).bool); default: throw new IllegalArgumentException("Unknow option " + getOptimizeLevel()); @@ -710,7 +715,7 @@ public CodeGenerator newCodeGenerator(AviatorClassLoader classLoader) { * @param expression * @return */ - public Expression compile(String expression) { + public Expression compile(final String expression) { return compile(expression, false); } @@ -723,7 +728,7 @@ public Expression compile(String expression) { * @param values * @return */ - public Object exec(String expression, Object... values) { + public Object exec(final String expression, final Object... values) { if (getOptimizeLevel() != AviatorEvaluator.EVAL) { throw new IllegalStateException("Aviator evaluator is not in EVAL mode."); } @@ -757,7 +762,8 @@ public Object exec(String expression, Object... values) { * @param env Binding variable environment * @param cached Whether to cache the compiled result,make true to cache it. */ - public Object execute(String expression, Map env, boolean cached) { + public Object execute(final String expression, final Map env, + final boolean cached) { Expression compiledExpression = compile(expression, cached); if (compiledExpression != null) { return compiledExpression.execute(env); @@ -774,7 +780,7 @@ public Object execute(String expression, Map env, boolean cached * @param env * @return */ - public Object execute(String expression, Map env) { + public Object execute(final String expression, final Map env) { return execute(expression, env, false); } @@ -784,8 +790,8 @@ public Object execute(String expression, Map env) { * * @param expression */ - public void invalidateCache(String expression) { - cacheExpressions.remove(expression); + public void invalidateCache(final String expression) { + this.cacheExpressions.remove(expression); } @@ -795,7 +801,7 @@ public void invalidateCache(String expression) { * @param expression * @return */ - public Object execute(String expression) { + public Object execute(final String expression) { return execute(expression, (Map) null); } } diff --git a/src/main/java/com/googlecode/aviator/runtime/RuntimeUtils.java b/src/main/java/com/googlecode/aviator/runtime/RuntimeUtils.java index 72a7cfc6..495541ba 100644 --- a/src/main/java/com/googlecode/aviator/runtime/RuntimeUtils.java +++ b/src/main/java/com/googlecode/aviator/runtime/RuntimeUtils.java @@ -65,4 +65,10 @@ public static AviatorFunction getFunction(Map env, String name) return getInstance(env).getFunction(name); } + public static void printStackTrace(final Map env, final Exception e) { + if (isTracedEval(env)) { + e.printStackTrace(); + } + } + } diff --git a/src/main/java/com/googlecode/aviator/runtime/function/seq/SeqContainsKeyFunction.java b/src/main/java/com/googlecode/aviator/runtime/function/seq/SeqContainsKeyFunction.java new file mode 100644 index 00000000..bab3c25f --- /dev/null +++ b/src/main/java/com/googlecode/aviator/runtime/function/seq/SeqContainsKeyFunction.java @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2010 dennis zhuang (killme2008@gmail.com) + * + * This library is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version + * 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + **/ +package com.googlecode.aviator.runtime.function.seq; + +import java.util.Map; +import com.googlecode.aviator.runtime.RuntimeUtils; +import com.googlecode.aviator.runtime.function.AbstractFunction; +import com.googlecode.aviator.runtime.type.AviatorBoolean; +import com.googlecode.aviator.runtime.type.AviatorObject; + + +/** + * seq.contains_key(map,key) function to check if seq(should be map) contains the key. + * + * @author dennis + * + */ +public class SeqContainsKeyFunction extends AbstractFunction { + + @SuppressWarnings("rawtypes") + @Override + public AviatorObject call(final Map env, final AviatorObject arg1, + final AviatorObject arg2) { + Object first = arg1.getValue(env); + if (first == null) { + throw new NullPointerException("null seq"); + } + Class clazz = first.getClass(); + if (Map.class.isAssignableFrom(clazz)) { + Map seq = (Map) first; + try { + return AviatorBoolean.valueOf(seq.containsKey(arg2.getValue(env))); + } catch (Exception e) { + RuntimeUtils.printStackTrace(env, e); + return AviatorBoolean.FALSE; + } + } else { + throw new IllegalArgumentException(arg1.desc(env) + " is not a map."); + } + } + + + @Override + public String getName() { + return "seq.contains_key"; + } + +} diff --git a/src/main/java/com/googlecode/aviator/runtime/function/seq/SeqIncludeFunction.java b/src/main/java/com/googlecode/aviator/runtime/function/seq/SeqIncludeFunction.java index 74e73ace..503073bc 100644 --- a/src/main/java/com/googlecode/aviator/runtime/function/seq/SeqIncludeFunction.java +++ b/src/main/java/com/googlecode/aviator/runtime/function/seq/SeqIncludeFunction.java @@ -18,6 +18,7 @@ import java.lang.reflect.Array; import java.util.Collection; import java.util.Map; +import com.googlecode.aviator.runtime.RuntimeUtils; import com.googlecode.aviator.runtime.function.AbstractFunction; import com.googlecode.aviator.runtime.type.AviatorBoolean; import com.googlecode.aviator.runtime.type.AviatorObject; @@ -26,14 +27,15 @@ /** * include(seq,obj) function to check if seq contains object - * + * * @author dennis - * + * */ public class SeqIncludeFunction extends AbstractFunction { @Override - public AviatorObject call(Map env, AviatorObject arg1, AviatorObject arg2) { + public AviatorObject call(final Map env, final AviatorObject arg1, + final AviatorObject arg2) { Object first = arg1.getValue(env); if (first == null) { throw new NullPointerException("null seq"); @@ -50,6 +52,7 @@ public AviatorObject call(Map env, AviatorObject arg1, AviatorOb } } } catch (Exception e) { + RuntimeUtils.printStackTrace(env, e); return AviatorBoolean.FALSE; } } else if (clazz.isArray()) { @@ -64,6 +67,7 @@ public AviatorObject call(Map env, AviatorObject arg1, AviatorOb } } } catch (Exception e) { + RuntimeUtils.printStackTrace(env, e); return AviatorBoolean.FALSE; } } else { diff --git a/src/main/java/com/googlecode/aviator/utils/Env.java b/src/main/java/com/googlecode/aviator/utils/Env.java index 820aec15..b8271460 100644 --- a/src/main/java/com/googlecode/aviator/utils/Env.java +++ b/src/main/java/com/googlecode/aviator/utils/Env.java @@ -118,20 +118,14 @@ public void clear() { /** * Check if a key has a defined value. This will return true if the key is present in - * the overrides map with a non-null value, or if the key is not present in the overrides map but - * is present in the defaults map. + * the overrides map or the defaults map. * * @param key * @return true if key defined, false if not */ @Override public boolean containsKey(final Object key) { - Map overrides = getmOverrides(true); - if (overrides.containsKey(key)) { - return overrides.get(key) != null; - } else { - return this.mDefaults.containsKey(key); - } + return getmOverrides(true).containsKey(key) || this.mDefaults.containsKey(key); } /** diff --git a/src/test/java/com/googlecode/aviator/test/function/FunctionTest.java b/src/test/java/com/googlecode/aviator/test/function/FunctionTest.java index 89879ca1..ae5154b4 100644 --- a/src/test/java/com/googlecode/aviator/test/function/FunctionTest.java +++ b/src/test/java/com/googlecode/aviator/test/function/FunctionTest.java @@ -1022,7 +1022,32 @@ public void testSeqNewList() { } } + @Test + public void testSeqContainsKey() { + assertEquals(Boolean.TRUE, AviatorEvaluator.execute("seq.contains_key(seq.map(1,2,3,4), 1)")); + assertEquals(Boolean.FALSE, AviatorEvaluator.execute("seq.contains_key(seq.map(1,2,3,4), 2)")); + assertEquals(Boolean.TRUE, AviatorEvaluator.execute("seq.contains_key(seq.map(1,2,3,4), 3)")); + assertEquals(Boolean.FALSE, AviatorEvaluator.execute("seq.contains_key(seq.map(1,2,3,4), 10)")); + + Map map = new HashMap<>(); + map.put("hello", 2L); + map.put(3L, 4L); + map.put("world", null); + + Map env = new HashMap<>(); + env.put("m", map); + + assertEquals(Boolean.TRUE, AviatorEvaluator.execute("seq.contains_key(m, 'hello')", env)); + assertEquals(Boolean.TRUE, AviatorEvaluator.execute("seq.contains_key(m, 'world')", env)); + assertEquals(Boolean.TRUE, AviatorEvaluator.execute("seq.contains_key(m, 3)", env)); + assertEquals(Boolean.FALSE, AviatorEvaluator.execute("seq.contains_key(m, 'test')", env)); + assertEquals(Boolean.FALSE, AviatorEvaluator.execute("seq.contains_key(m, -1)", env)); + } + @Test(expected = ExpressionRuntimeException.class) + public void testSeqContainsKeyWrongType() { + AviatorEvaluator.execute("seq.contains_key(seq.list(), 'hello')"); + } @Test public void testSeqNewMap() { From 443c921ef30c2104ef4e630f35f6a810b1d325f8 Mon Sep 17 00:00:00 2001 From: dennis Date: Sun, 26 May 2019 16:08:42 +0800 Subject: [PATCH 7/8] (fix) docs --- src/main/java/com/googlecode/aviator/Options.java | 4 ++-- .../java/com/googlecode/aviator/runtime/FunctionArgument.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/googlecode/aviator/Options.java b/src/main/java/com/googlecode/aviator/Options.java index 2f24700f..f4f1b52a 100644 --- a/src/main/java/com/googlecode/aviator/Options.java +++ b/src/main/java/com/googlecode/aviator/Options.java @@ -63,8 +63,8 @@ public enum Options { PUT_CAPTURING_GROUPS_INTO_ENV, /** - * Whether to capture the function arguments(invocation) into env, the parameter list will be - * stored in __args__ variable in function body. Default is false(disabled). + * Whether to capture the function arguments(at invocation) into env, the argument list will be + * stored in __args__ variable in env valid for function body. Default is false(disabled). * * @since 4.2.0 */ diff --git a/src/main/java/com/googlecode/aviator/runtime/FunctionArgument.java b/src/main/java/com/googlecode/aviator/runtime/FunctionArgument.java index 05c37eea..26942db9 100644 --- a/src/main/java/com/googlecode/aviator/runtime/FunctionArgument.java +++ b/src/main/java/com/googlecode/aviator/runtime/FunctionArgument.java @@ -1,8 +1,9 @@ package com.googlecode.aviator.runtime; /** - * A function parameter + * A function argument. * + * @since 4.2.0 * @author dennis(killme2008@gmail.com) * */ From 3baab9a65a6a77d4f626b2c7a4a9f1aaa7d75068 Mon Sep 17 00:00:00 2001 From: dennis Date: Sun, 26 May 2019 16:10:55 +0800 Subject: [PATCH 8/8] (feat) Unmodify func args info --- src/main/java/com/googlecode/aviator/BaseExpression.java | 2 +- .../java/com/googlecode/aviator/code/asm/ASMCodeGenerator.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/googlecode/aviator/BaseExpression.java b/src/main/java/com/googlecode/aviator/BaseExpression.java index 161ea027..e56011e5 100644 --- a/src/main/java/com/googlecode/aviator/BaseExpression.java +++ b/src/main/java/com/googlecode/aviator/BaseExpression.java @@ -45,7 +45,7 @@ public BaseExpression(final AviatorEvaluatorInstance instance, final List> funcsArgs) { if (funcsArgs != null) { - this.funcsArgs = funcsArgs; + this.funcsArgs = Collections.unmodifiableMap(funcsArgs); } } 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 2b5d776e..0964b1ed 100644 --- a/src/main/java/com/googlecode/aviator/code/asm/ASMCodeGenerator.java +++ b/src/main/java/com/googlecode/aviator/code/asm/ASMCodeGenerator.java @@ -961,7 +961,7 @@ public void onMethodInvoke(final Token lookhead, final List if (this.instance.getOptionValue(Options.CAPTURE_FUNCTION_ARGS).bool) { if (params != null && !params.isEmpty()) { int funcId = getNextFuncInvocationId(); - getFuncsArgs().put(funcId, params); + getFuncsArgs().put(funcId, Collections.unmodifiableList(params)); loadEnv(); this.mv.visitLdcInsn(FUNC_ARGS_INNER_VAR); this.mv.visitLdcInsn(funcId);