From d097db8edfef2b9102881db701db733cecf2f531 Mon Sep 17 00:00:00 2001 From: Dennis Zhuang Date: Tue, 3 Oct 2023 17:14:03 +0800 Subject: [PATCH] fix: elsif parser #554, #564 and serialize with constants --- .../aviator/AviatorEvaluatorInstance.java | 51 +------------------ .../googlecode/aviator/BaseExpression.java | 3 +- .../googlecode/aviator/FunctionMissing.java | 3 +- .../googlecode/aviator/lexer/SymbolTable.java | 10 ++++ .../aviator/parser/ExpressionParser.java | 7 +-- .../JavaMethodReflectionFunctionMissing.java | 2 + .../runtime/function/ClassMethodFunction.java | 47 +++++++++++++++-- .../function/internal/ReducerFunction.java | 1 - .../function/internal/ReducerResult.java | 8 ++- .../aviator/runtime/type/AviatorBoolean.java | 3 ++ .../aviator/runtime/type/Range.java | 9 ++++ .../serialize/AviatorObjectInputStream.java | 30 +++++++++++ .../serialize/AviatorObjectOutputStream.java | 2 + .../com/googlecode/aviator/utils/Env.java | 12 ++--- .../googlecode/aviator/utils/Reflector.java | 50 ++++++++++++++++++ .../com/googlecode/aviator/TestUtils.java | 4 ++ .../aviator/scripts/TestScripts.java | 48 +++++++++++++++-- .../TestScriptsWithIntepreterSerialize.java | 22 ++++++++ .../scripts/TestScriptsWithSerialize.java | 22 ++++++++ src/test/resources/scripts/if_elsif4.av | 8 +++ src/test/resources/scripts/if_elsif5.av | 16 ++++++ 21 files changed, 287 insertions(+), 71 deletions(-) create mode 100644 src/test/java/com/googlecode/aviator/scripts/TestScriptsWithIntepreterSerialize.java create mode 100644 src/test/java/com/googlecode/aviator/scripts/TestScriptsWithSerialize.java create mode 100644 src/test/resources/scripts/if_elsif4.av create mode 100644 src/test/resources/scripts/if_elsif5.av diff --git a/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java b/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java index f5e730a1..6687daeb 100644 --- a/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java +++ b/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java @@ -28,7 +28,6 @@ import java.io.OutputStream; import java.io.Reader; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.net.URL; import java.nio.charset.Charset; import java.security.AccessController; @@ -493,7 +492,7 @@ public void removeModule(final String ns) { private Env loadModule(final Class moduleClazz) throws IllegalAccessException, NoSuchMethodException { - Map> methodMap = findMethodsFromClass(moduleClazz, true); + Map> methodMap = Reflector.findMethodsFromClass(moduleClazz, true); if (methodMap == null || methodMap.isEmpty()) { throw new IllegalArgumentException("Empty module"); @@ -564,7 +563,7 @@ public List addInstanceFunctions(final String namespace, final Class private List addMethodFunctions(final String namespace, final boolean isStatic, final Class clazz) throws IllegalAccessException, NoSuchMethodException { - Map> methodMap = findMethodsFromClass(clazz, isStatic); + Map> methodMap = Reflector.findMethodsFromClass(clazz, isStatic); List added = new ArrayList<>(); for (Map.Entry> entry : methodMap.entrySet()) { @@ -643,52 +642,6 @@ public List importFunctions(final Class clazz) return result; } - private Map> findMethodsFromClass(final Class clazz, - final boolean isStatic) { - Map> methodMap = new HashMap<>(); - - for (Method method : clazz.getMethods()) { - int modifiers = method.getModifiers(); - if (Modifier.isPublic(modifiers)) { - if (isStatic) { - if (!Modifier.isStatic(modifiers)) { - continue; - } - } else { - if (Modifier.isStatic(modifiers)) { - continue; - } - } - - if (method.getAnnotation(Ignore.class) != null) { - continue; - } - - String methodName = method.getName(); - Function func = method.getAnnotation(Function.class); - if (func != null) { - String rename = func.rename(); - if (!rename.isEmpty()) { - if (!ExpressionParser.isJavaIdentifier(rename)) { - throw new IllegalArgumentException("Invalid rename `" + rename + "` for method " - + method.getName() + " in class " + clazz); - } - methodName = func.rename(); - } - } - - List methods = methodMap.get(methodName); - if (methods == null) { - methods = new ArrayList<>(3); - methodMap.put(methodName, methods); - } - methods.add(method); - } - } - - return methodMap; - } - /** * Remove a function loader * diff --git a/src/main/java/com/googlecode/aviator/BaseExpression.java b/src/main/java/com/googlecode/aviator/BaseExpression.java index a1b916ce..f664a948 100644 --- a/src/main/java/com/googlecode/aviator/BaseExpression.java +++ b/src/main/java/com/googlecode/aviator/BaseExpression.java @@ -49,7 +49,7 @@ public abstract class BaseExpression implements Expression { private Map> funcsArgs = Collections.emptyMap(); protected SymbolTable symbolTable; // cached compiled string segments for string interpolation. - private transient final ConcurrentHashMap> stringSegs = + private transient ConcurrentHashMap> stringSegs = new ConcurrentHashMap>(); protected String sourceFile; @@ -383,6 +383,7 @@ public void customReadObject(ObjectInputStream input) throws ClassNotFoundExcept this.symbolTable = (SymbolTable) input.readObject(); this.sourceFile = (String) input.readObject(); this.lambdaBootstraps = (Map) input.readObject(); + this.stringSegs = new ConcurrentHashMap>(); } public void customWriteObject(ObjectOutputStream output) throws IOException { diff --git a/src/main/java/com/googlecode/aviator/FunctionMissing.java b/src/main/java/com/googlecode/aviator/FunctionMissing.java index fe7013db..311a087e 100644 --- a/src/main/java/com/googlecode/aviator/FunctionMissing.java +++ b/src/main/java/com/googlecode/aviator/FunctionMissing.java @@ -8,6 +8,7 @@ * */ +import java.io.Serializable; import java.util.Map; import com.googlecode.aviator.runtime.type.AviatorObject; @@ -22,7 +23,7 @@ * @since 4.2.5 * */ -public interface FunctionMissing { +public interface FunctionMissing extends Serializable { /** * Called when function not found, return the invocation result. * diff --git a/src/main/java/com/googlecode/aviator/lexer/SymbolTable.java b/src/main/java/com/googlecode/aviator/lexer/SymbolTable.java index 5a0d1bb5..cd759dc1 100644 --- a/src/main/java/com/googlecode/aviator/lexer/SymbolTable.java +++ b/src/main/java/com/googlecode/aviator/lexer/SymbolTable.java @@ -84,6 +84,16 @@ public boolean isReserved(final String name) { return isReservedKeyword(name) || this.table.containsKey(name); } + /** + * Try to reserve key word, return the reserved variable if success, otherwise return itself. + * + * @param var + * @return + */ + public static Variable tryReserveKeyword(final Variable var) { + Variable reserve = RESERVED.get(var.getLexeme()); + return reserve != null ? reserve : var; + } /** * Get variable by name diff --git a/src/main/java/com/googlecode/aviator/parser/ExpressionParser.java b/src/main/java/com/googlecode/aviator/parser/ExpressionParser.java index fdb10285..62b37d8e 100644 --- a/src/main/java/com/googlecode/aviator/parser/ExpressionParser.java +++ b/src/main/java/com/googlecode/aviator/parser/ExpressionParser.java @@ -1965,7 +1965,7 @@ private boolean ifStatement(final boolean isWhile, final boolean isElsif) { this.scope.leaveBrace(); move(true); - elseBodyHasReturn = elseStatement(isWhile, ifBodyHasReturn); + elseBodyHasReturn = elseStatement(isWhile, isElsif, ifBodyHasReturn); getCodeGeneratorWithTimes().onMethodParameter(this.lookhead); } @@ -2008,7 +2008,8 @@ private String getLoopKeyword(final boolean isWhile) { return isWhile ? "while" : "if"; } - private boolean elseStatement(final boolean isWhile, final boolean ifBodyHasReturn) { + private boolean elseStatement(final boolean isWhile, boolean isElsif, + final boolean ifBodyHasReturn) { if (isWhile) { // Call __reducer_break(nil) final CodeGenerator cg = getCodeGeneratorWithTimes(); @@ -2046,7 +2047,7 @@ private boolean elseStatement(final boolean isWhile, final boolean ifBodyHasRetu } else if (hasElsif) { hasReturn = ifStatement(false, true); getCodeGenerator().onTernaryRight(this.lookhead); - } else if (ifBodyHasReturn) { + } else if (ifBodyHasReturn && !isElsif) { hasReturn = elseBody(true); } else { return withoutElse(); diff --git a/src/main/java/com/googlecode/aviator/runtime/JavaMethodReflectionFunctionMissing.java b/src/main/java/com/googlecode/aviator/runtime/JavaMethodReflectionFunctionMissing.java index 9946a272..8c3c90cf 100644 --- a/src/main/java/com/googlecode/aviator/runtime/JavaMethodReflectionFunctionMissing.java +++ b/src/main/java/com/googlecode/aviator/runtime/JavaMethodReflectionFunctionMissing.java @@ -18,6 +18,8 @@ */ public class JavaMethodReflectionFunctionMissing implements FunctionMissing { + private static final long serialVersionUID = -7829608231403725185L; + private JavaMethodReflectionFunctionMissing() { } diff --git a/src/main/java/com/googlecode/aviator/runtime/function/ClassMethodFunction.java b/src/main/java/com/googlecode/aviator/runtime/function/ClassMethodFunction.java index 87c0976d..8adff690 100644 --- a/src/main/java/com/googlecode/aviator/runtime/function/ClassMethodFunction.java +++ b/src/main/java/com/googlecode/aviator/runtime/function/ClassMethodFunction.java @@ -1,10 +1,18 @@ package com.googlecode.aviator.runtime.function; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; +import java.util.Collections; import java.util.List; import java.util.Map; +import com.googlecode.aviator.code.interpreter.IR; +import com.googlecode.aviator.lexer.token.Token; +import com.googlecode.aviator.parser.VariableMeta; +import com.googlecode.aviator.runtime.type.AviatorJavaType; import com.googlecode.aviator.runtime.type.AviatorObject; import com.googlecode.aviator.utils.Reflector; @@ -20,11 +28,11 @@ public class ClassMethodFunction extends AbstractVariadicFunction { private static final long serialVersionUID = 5946505010078966461L; private MethodHandle handle; // Only for one-arity function. private Class[] pTypes; - private final String name; - private final String methodName; + private String name; + private String methodName; private List methods; // For reflection. - private final Class clazz; - private final boolean isStatic; + private Class clazz; + private boolean isStatic; public ClassMethodFunction(final Class clazz, final boolean isStatic, final String name, final String methodName, final List methods) @@ -34,6 +42,11 @@ public ClassMethodFunction(final Class clazz, final boolean isStatic, final S this.isStatic = isStatic; this.methodName = methodName; + init(isStatic, methodName, methods); + } + + private void init(final boolean isStatic, final String methodName, final List methods) + throws IllegalAccessException, NoSuchMethodException { if (methods.size() == 1) { // fast path by method handle. this.handle = MethodHandles.lookup().unreflect(methods.get(0)).asFixedArity(); @@ -53,6 +66,32 @@ public ClassMethodFunction(final Class clazz, final boolean isStatic, final S } } + private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException { + this.name = (String) input.readObject(); + this.clazz = (Class) input.readObject(); + this.isStatic = input.readBoolean(); + this.methodName = (String) input.readObject(); + + Map> allMethods = Reflector.findMethodsFromClass(clazz, isStatic); + + List methods = allMethods.get(this.methodName); + if (methods == null) { + methods = Collections.emptyList(); + } + + try { + this.init(this.isStatic, this.methodName, methods); + } catch (Throwable t) { + throw Reflector.sneakyThrow(t); + } + } + + private void writeObject(ObjectOutputStream output) throws IOException { + output.writeObject(this.name); + output.writeObject(this.clazz); + output.writeBoolean(this.isStatic); + output.writeObject(this.methodName); + } @Override public String getName() { diff --git a/src/main/java/com/googlecode/aviator/runtime/function/internal/ReducerFunction.java b/src/main/java/com/googlecode/aviator/runtime/function/internal/ReducerFunction.java index 1906f178..669b6929 100644 --- a/src/main/java/com/googlecode/aviator/runtime/function/internal/ReducerFunction.java +++ b/src/main/java/com/googlecode/aviator/runtime/function/internal/ReducerFunction.java @@ -54,7 +54,6 @@ private AviatorObject reduce(final Map env, final AviatorObject long c = 0; if (coll != Range.LOOP) { - long arities = (long) arg2.meta(Constants.ARITIES_META); long index = 0; boolean unboxEntry = diff --git a/src/main/java/com/googlecode/aviator/runtime/function/internal/ReducerResult.java b/src/main/java/com/googlecode/aviator/runtime/function/internal/ReducerResult.java index de93372c..6caee509 100644 --- a/src/main/java/com/googlecode/aviator/runtime/function/internal/ReducerResult.java +++ b/src/main/java/com/googlecode/aviator/runtime/function/internal/ReducerResult.java @@ -4,6 +4,7 @@ import com.googlecode.aviator.runtime.type.AviatorObject; import com.googlecode.aviator.runtime.type.AviatorRuntimeJavaType; import com.googlecode.aviator.runtime.type.AviatorType; +import com.googlecode.aviator.utils.Env; /** * ReducerResult in looping. @@ -70,7 +71,12 @@ public Object getValue(final Map env) { @Override public String toString() { - return this.obj.toString(); + Object val = getValue(Env.EMPTY_ENV); + if (val != this) { + return ""; + } else { + return ""; + } } @Override diff --git a/src/main/java/com/googlecode/aviator/runtime/type/AviatorBoolean.java b/src/main/java/com/googlecode/aviator/runtime/type/AviatorBoolean.java index 31c52869..ea0d89ed 100644 --- a/src/main/java/com/googlecode/aviator/runtime/type/AviatorBoolean.java +++ b/src/main/java/com/googlecode/aviator/runtime/type/AviatorBoolean.java @@ -51,6 +51,9 @@ public final boolean booleanValue(final Map env) { return this.value.booleanValue(); } + public boolean getBooleanValue() { + return this.value; + } @Override public AviatorObject add(final AviatorObject other, final Map env) { diff --git a/src/main/java/com/googlecode/aviator/runtime/type/Range.java b/src/main/java/com/googlecode/aviator/runtime/type/Range.java index 492f5a5a..607d73f1 100644 --- a/src/main/java/com/googlecode/aviator/runtime/type/Range.java +++ b/src/main/java/com/googlecode/aviator/runtime/type/Range.java @@ -23,11 +23,19 @@ public final class Range extends AviatorObject implements Sequence { private static final AviatorLong ZERO = AviatorLong.valueOf(0L); public static final Range LOOP = new Range(ZERO, ZERO, ZERO); + static { + LOOP.isLoop = true; + } + private final AviatorNumber step; private final AviatorNumber start; private final AviatorNumber end; final boolean forward; + private boolean isLoop; + public boolean isLoop() { + return this.isLoop; + } public Range(final AviatorNumber start, final AviatorNumber end, final AviatorNumber step) { super(); @@ -35,6 +43,7 @@ public Range(final AviatorNumber start, final AviatorNumber end, final AviatorNu this.end = end; this.step = step; this.forward = Range.this.step.compare(ZERO, Env.EMPTY_ENV) >= 0; + this.isLoop = false; } @Override diff --git a/src/main/java/com/googlecode/aviator/serialize/AviatorObjectInputStream.java b/src/main/java/com/googlecode/aviator/serialize/AviatorObjectInputStream.java index 02902d18..2b2ed46f 100644 --- a/src/main/java/com/googlecode/aviator/serialize/AviatorObjectInputStream.java +++ b/src/main/java/com/googlecode/aviator/serialize/AviatorObjectInputStream.java @@ -12,7 +12,13 @@ import com.googlecode.aviator.ClassExpression; import com.googlecode.aviator.Expression; import com.googlecode.aviator.code.asm.ClassDefiner; +import com.googlecode.aviator.lexer.SymbolTable; +import com.googlecode.aviator.lexer.token.Variable; import com.googlecode.aviator.parser.AviatorClassLoader; +import com.googlecode.aviator.runtime.type.AviatorBigInt; +import com.googlecode.aviator.runtime.type.AviatorBoolean; +import com.googlecode.aviator.runtime.type.AviatorNil; +import com.googlecode.aviator.runtime.type.Range; import com.googlecode.aviator.utils.Reflector; /** @@ -50,6 +56,30 @@ protected Object resolveObject(Object obj) throws IOException { .setClassBytes(this.classBytesCache.get(object.getClass().getName())); } } + + // Processing some internal constants. + if (object instanceof AviatorBoolean) { + AviatorBoolean bool = (AviatorBoolean) object; + if (bool.getBooleanValue()) { + object = AviatorBoolean.TRUE; + } else { + object = AviatorBoolean.FALSE; + } + } + if (object instanceof AviatorNil) { + object = AviatorNil.NIL; + } + + if (object instanceof Range) { + if (((Range) object).isLoop()) { + object = Range.LOOP; + } + } + + if (object instanceof Variable) { + object = SymbolTable.tryReserveKeyword((Variable) object); + } + return object; } diff --git a/src/main/java/com/googlecode/aviator/serialize/AviatorObjectOutputStream.java b/src/main/java/com/googlecode/aviator/serialize/AviatorObjectOutputStream.java index ffb1a7df..757710a8 100644 --- a/src/main/java/com/googlecode/aviator/serialize/AviatorObjectOutputStream.java +++ b/src/main/java/com/googlecode/aviator/serialize/AviatorObjectOutputStream.java @@ -7,6 +7,7 @@ import java.util.HashMap; import java.util.Map; import com.googlecode.aviator.ClassExpression; +import com.googlecode.aviator.runtime.type.Range; /** * A special ObjectOutputStream that will write the generated script class byte array. @@ -30,6 +31,7 @@ protected Object replaceObject(Object obj) throws IOException { if (obj instanceof ClassExpression) { this.classBytesCache.put(obj.getClass().getName(), ((ClassExpression) obj).getClassBytes()); } + return super.replaceObject(obj); } diff --git a/src/main/java/com/googlecode/aviator/utils/Env.java b/src/main/java/com/googlecode/aviator/utils/Env.java index 964742a1..cc00c175 100644 --- a/src/main/java/com/googlecode/aviator/utils/Env.java +++ b/src/main/java/com/googlecode/aviator/utils/Env.java @@ -334,26 +334,26 @@ public Set> entrySet() { public Object get(final Object key) { // Should check ENV_VAR at first // TODO: performance tweak - if (Constants.REDUCER_LOOP_VAR == key) { + if (Constants.REDUCER_LOOP_VAR.equals(key)) { return Range.LOOP; } - if (Constants.REDUCER_EMPTY_VAR == key) { + if (Constants.REDUCER_EMPTY_VAR.equals(key)) { return ReducerResult.withEmpty(AviatorNil.NIL); } - if (Constants.ENV_VAR == key) { + if (Constants.ENV_VAR.equals(key)) { this.instance.ensureFeatureEnabled(Feature.InternalVars); return this; } - if (Constants.FUNC_ARGS_VAR == key) { + if (Constants.FUNC_ARGS_VAR.equals(key)) { this.instance.ensureFeatureEnabled(Feature.InternalVars); return FunctionUtils.getFunctionArguments(this); } - if (Constants.INSTANCE_VAR == key) { + if (Constants.INSTANCE_VAR.equals(key)) { this.instance.ensureFeatureEnabled(Feature.InternalVars); return this.instance; } - if (Constants.EXP_VAR == key) { + if (Constants.EXP_VAR.equals(key)) { this.instance.ensureFeatureEnabled(Feature.InternalVars); return this.expression; } diff --git a/src/main/java/com/googlecode/aviator/utils/Reflector.java b/src/main/java/com/googlecode/aviator/utils/Reflector.java index ec4553d8..940f3bad 100644 --- a/src/main/java/com/googlecode/aviator/utils/Reflector.java +++ b/src/main/java/com/googlecode/aviator/utils/Reflector.java @@ -22,6 +22,7 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -30,7 +31,10 @@ import java.util.concurrent.ConcurrentHashMap; import com.googlecode.aviator.AviatorEvaluatorInstance; import com.googlecode.aviator.Feature; +import com.googlecode.aviator.annotation.Function; +import com.googlecode.aviator.annotation.Ignore; import com.googlecode.aviator.exception.NoSuchPropertyException; +import com.googlecode.aviator.parser.ExpressionParser; import com.googlecode.aviator.runtime.RuntimeUtils; import com.googlecode.aviator.runtime.function.ClassMethodFunction; import com.googlecode.aviator.runtime.type.AviatorJavaType; @@ -870,4 +874,50 @@ public static Object fastGetProperty(final String name, final String[] names, } return throwNoSuchPropertyException("Variable `" + name + "` not found in env: " + env); } + + public static Map> findMethodsFromClass(final Class clazz, + final boolean isStatic) { + Map> methodMap = new HashMap<>(); + + for (Method method : clazz.getMethods()) { + int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers)) { + if (isStatic) { + if (!Modifier.isStatic(modifiers)) { + continue; + } + } else { + if (Modifier.isStatic(modifiers)) { + continue; + } + } + + if (method.getAnnotation(Ignore.class) != null) { + continue; + } + + String methodName = method.getName(); + Function func = method.getAnnotation(Function.class); + if (func != null) { + String rename = func.rename(); + if (!rename.isEmpty()) { + if (!ExpressionParser.isJavaIdentifier(rename)) { + throw new IllegalArgumentException("Invalid rename `" + rename + "` for method " + + method.getName() + " in class " + clazz); + } + methodName = func.rename(); + } + } + + List methods = methodMap.get(methodName); + if (methods == null) { + methods = new ArrayList<>(3); + methodMap.put(methodName, methods); + } + methods.add(method); + } + } + + return methodMap; + } } diff --git a/src/test/java/com/googlecode/aviator/TestUtils.java b/src/test/java/com/googlecode/aviator/TestUtils.java index ecdfd136..857c128b 100644 --- a/src/test/java/com/googlecode/aviator/TestUtils.java +++ b/src/test/java/com/googlecode/aviator/TestUtils.java @@ -71,6 +71,10 @@ public static void assertArrayEquals(final Object[] expected, final Object[] rea } public static void assertEquals(final Object expected, final Object real) { + if (expected == real) { + return; + } + if (expected instanceof Number && real instanceof Number) { if (TypeUtils.isDouble(expected) && TypeUtils.isDouble(real)) { double delta = ((Number) expected).doubleValue() - ((Number) real).doubleValue(); diff --git a/src/test/java/com/googlecode/aviator/scripts/TestScripts.java b/src/test/java/com/googlecode/aviator/scripts/TestScripts.java index 69823216..49ab1b16 100644 --- a/src/test/java/com/googlecode/aviator/scripts/TestScripts.java +++ b/src/test/java/com/googlecode/aviator/scripts/TestScripts.java @@ -2,9 +2,14 @@ import static com.googlecode.aviator.TestUtils.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.List; import java.util.Random; @@ -13,6 +18,7 @@ import com.googlecode.aviator.AviatorEvaluator; import com.googlecode.aviator.AviatorEvaluatorInstance; import com.googlecode.aviator.Expression; +import com.googlecode.aviator.Options; import com.googlecode.aviator.TestUtils; import com.googlecode.aviator.exception.ExpressionSyntaxErrorException; import com.googlecode.aviator.exception.StandardError; @@ -26,6 +32,8 @@ public class TestScripts { protected AviatorEvaluatorInstance instance; + protected boolean testSerialize = false; + @Before public void setup() throws Exception { this.instance = AviatorEvaluator.newInstance(); @@ -35,11 +43,37 @@ public void setup() throws Exception { public Object testScript(final String name, final Object... args) { try { - System.out.println("Testing script " + name + " with args: " + Arrays.toString(args)); + System.out.println("Testing script(testSerialize=" + this.testSerialize + ") " + name + + " with args: " + Arrays.toString(args)); final String file = TestScripts.class.getResource("/scripts/" + name).getFile(); this.instance.validate(IoModule.slurp(file)); Expression exp = this.instance.compileScript(file, true); - return exp.execute(AviatorEvaluator.newEnv(args)); + + Object result = exp.execute(AviatorEvaluator.newEnv(args)); + if (testSerialize) { + // test serialize/deserialize + byte[] bs = null; + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + ObjectOutputStream output = instance.newObjectOutputStream(out); + output.writeObject(exp); + output.close(); + bs = out.toByteArray(); + } + + assertNotNull(bs); + + try (ByteArrayInputStream in = new ByteArrayInputStream(bs)) { + ObjectInputStream input = instance.newObjectInputStream(in); + Expression deExp = (Expression) input.readObject(); + assertNotSame(deExp, exp); + Object resultDes = deExp.execute(AviatorEvaluator.newEnv(args)); + // return the result by deserialized expression executed. + return resultDes; + } + } else { + return result; + } + } catch (Throwable t) { Reflector.sneakyThrow(t); } @@ -59,7 +93,6 @@ public Object testLib(final String name, final Object... args) { return null; } - @Test public void testLibs() { testLib("aviator"); @@ -137,7 +170,6 @@ public void testRange() { assertTrue(testScript("range.av") instanceof Range); } - @Test public void testStringSeq() { assertEquals("hello world", testScript("string_seq.av")); @@ -190,6 +222,13 @@ public void testIfElse() { assertEquals("a is less than 10.", testScript("if_elsif3.av", "a", 8)); assertEquals("a is greater than 10.", testScript("if_elsif3.av", "a", 12)); assertEquals("statement after if", testScript("if_elsif3.av", "a", 112)); + assertEquals(2, testScript("if_elsif4.av", "a", 1)); + assertEquals(1, testScript("if_elsif4.av", "a", 0)); + assertEquals( + Arrays.asList("condition1", "end0", "condition1", "end1", "condition2", "end2", + "condition3", "condition1", "end4", "condition1", "end5", "condition1", "end6", + "condition1", "end7", "condition1", "end8", "condition1", "end9"), + testScript("if_elsif5.av")); assertEquals(7, testScript("if_else7.av")); assertEquals(8, testScript("if_else8.av")); @@ -355,7 +394,6 @@ public void testFunctions() { assertSortArray((int[]) testScript("selection_sort.av", "a", a), i); } - // test fibonacci assertEquals(0, testScript("fibonacci.av", "n", 0)); assertEquals(1, testScript("fibonacci.av", "n", 1)); diff --git a/src/test/java/com/googlecode/aviator/scripts/TestScriptsWithIntepreterSerialize.java b/src/test/java/com/googlecode/aviator/scripts/TestScriptsWithIntepreterSerialize.java new file mode 100644 index 00000000..4db90469 --- /dev/null +++ b/src/test/java/com/googlecode/aviator/scripts/TestScriptsWithIntepreterSerialize.java @@ -0,0 +1,22 @@ +package com.googlecode.aviator.scripts; + +import org.junit.Before; +import com.googlecode.aviator.AviatorEvaluator; +import com.googlecode.aviator.EvalMode; +import com.googlecode.aviator.Options; +import com.googlecode.aviator.TestUtils; +import com.googlecode.aviator.runtime.JavaMethodReflectionFunctionMissing; + +public class TestScriptsWithIntepreterSerialize extends TestScripts { + + @Override + @Before + public void setup() throws Exception { + this.instance = AviatorEvaluator.newInstance(EvalMode.INTERPRETER); + this.instance.setOption(Options.SERIALIZABLE, true); + this.instance.addStaticFunctions("j", TestUtils.class); + this.instance.setFunctionMissing(JavaMethodReflectionFunctionMissing.getInstance()); + this.testSerialize = true; + } + +} diff --git a/src/test/java/com/googlecode/aviator/scripts/TestScriptsWithSerialize.java b/src/test/java/com/googlecode/aviator/scripts/TestScriptsWithSerialize.java new file mode 100644 index 00000000..4e8cf21f --- /dev/null +++ b/src/test/java/com/googlecode/aviator/scripts/TestScriptsWithSerialize.java @@ -0,0 +1,22 @@ +package com.googlecode.aviator.scripts; + +import org.junit.Before; +import com.googlecode.aviator.AviatorEvaluator; +import com.googlecode.aviator.EvalMode; +import com.googlecode.aviator.Options; +import com.googlecode.aviator.TestUtils; +import com.googlecode.aviator.runtime.JavaMethodReflectionFunctionMissing; + +public class TestScriptsWithSerialize extends TestScripts { + + @Override + @Before + public void setup() throws Exception { + this.instance = AviatorEvaluator.newInstance(EvalMode.ASM); + this.instance.setOption(Options.SERIALIZABLE, true); + this.instance.addStaticFunctions("j", TestUtils.class); + this.instance.setFunctionMissing(JavaMethodReflectionFunctionMissing.getInstance()); + this.testSerialize = true; + } + +} diff --git a/src/test/resources/scripts/if_elsif4.av b/src/test/resources/scripts/if_elsif4.av new file mode 100644 index 00000000..6455ae09 --- /dev/null +++ b/src/test/resources/scripts/if_elsif4.av @@ -0,0 +1,8 @@ +let result = nil; +if a == 1 { + result = 2; +} elsif a == 0 { + return 1; +} + +return result; \ No newline at end of file diff --git a/src/test/resources/scripts/if_elsif5.av b/src/test/resources/scripts/if_elsif5.av new file mode 100644 index 00000000..2f841e1c --- /dev/null +++ b/src/test/resources/scripts/if_elsif5.av @@ -0,0 +1,16 @@ +let list = seq.list(); + +for i in range(0, 10) { + if (i != 3 && i != 2) { + seq.add(list, "condition1"); + } elsif(i != 3) { + seq.add(list, "condition2"); + } elsif(i != 2) { + seq.add(list, "condition3"); + continue; + } + + seq.add(list, "end"+i); +} + +return list; \ No newline at end of file