diff --git a/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java b/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java index ddf4fa71..2dddd77b 100644 --- a/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java +++ b/src/main/java/com/googlecode/aviator/AviatorEvaluatorInstance.java @@ -1020,6 +1020,7 @@ private void loadSystemFunctions() { private void loadInternalLibs() { if (getEvalMode() == EvalMode.ASM) { + if (internalASMLibFunctions == null) { internalASMLibFunctions = loadInternalFunctions(); // cache it } else { @@ -1081,10 +1082,18 @@ private Map loadInternalFunctions() { AviatorEvaluatorInstance(final EvalMode evalMode) { fillDefaultOpts(); setOption(Options.EVAL_MODE, evalMode); - loadFeatureFunctions(); - loadLib(); - loadModule(); - addFunctionLoader(ClassPathConfigFunctionLoader.getInstance()); + + // Load libs with Options.SERIALIZABLE=true + boolean serializable = this.getOptionValue(Options.SERIALIZABLE).bool; + try { + this.setOption(Options.SERIALIZABLE, true); + loadFeatureFunctions(); + loadLib(); + loadModule(); + addFunctionLoader(ClassPathConfigFunctionLoader.getInstance()); + } finally { + this.setOption(Options.SERIALIZABLE, serializable); + } } private void fillDefaultOpts() { diff --git a/src/main/java/com/googlecode/aviator/code/LambdaGenerator.java b/src/main/java/com/googlecode/aviator/code/LambdaGenerator.java index ede3c6f9..ff1f527f 100644 --- a/src/main/java/com/googlecode/aviator/code/LambdaGenerator.java +++ b/src/main/java/com/googlecode/aviator/code/LambdaGenerator.java @@ -52,7 +52,7 @@ public LambdaGenerator(final AviatorEvaluatorInstance instance, this.inheritEnv = inheritEnv; // Generate lambda class name this.className = - "Lambda_" + System.currentTimeMillis() + "_" + LAMBDA_COUNTER.getAndIncrement(); + "AviatorScript_" + System.currentTimeMillis() + "_" + LAMBDA_COUNTER.getAndIncrement(); // Auto compute frames // this.classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); // visitClass(); 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 5f2f0612..85136b70 100644 --- a/src/main/java/com/googlecode/aviator/runtime/function/LambdaFunction.java +++ b/src/main/java/com/googlecode/aviator/runtime/function/LambdaFunction.java @@ -245,7 +245,7 @@ protected Map newEnv(final Map parentEnv, env = new Env(contextEnv); env.configure(this.context.getInstance(), this.expression); } else { - assert (parentEnv == this.context); + // assert (parentEnv == this.context); env = (Env) parentEnv; } diff --git a/src/main/java/com/googlecode/aviator/runtime/type/AviatorJavaType.java b/src/main/java/com/googlecode/aviator/runtime/type/AviatorJavaType.java index eb4f512e..dcd9929c 100644 --- a/src/main/java/com/googlecode/aviator/runtime/type/AviatorJavaType.java +++ b/src/main/java/com/googlecode/aviator/runtime/type/AviatorJavaType.java @@ -15,6 +15,9 @@ **/ package com.googlecode.aviator.runtime.type; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; @@ -45,8 +48,20 @@ public class AviatorJavaType extends AviatorObject { private static final long serialVersionUID = -4353225521490659987L; protected String name; - private final boolean containsDot; + private boolean containsDot; private String[] subNames; + private SymbolTable symbolTable; + + private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException { + String name = (String) input.readObject(); + SymbolTable symbolTable = (SymbolTable) input.readObject(); + init(name, symbolTable); + } + + private void writeObject(ObjectOutputStream output) throws IOException { + output.writeObject(this.name); + output.writeObject(this.symbolTable); + } @Override public AviatorType getAviatorType() { @@ -63,6 +78,10 @@ public AviatorJavaType(final String name) { public AviatorJavaType(final String name, final SymbolTable symbolTable) { super(); + init(name, symbolTable); + } + + private void init(final String name, final SymbolTable symbolTable) { if (name != null) { String rName = reserveName(name); if (rName != null) { @@ -79,6 +98,7 @@ public AviatorJavaType(final String name, final SymbolTable symbolTable) { this.name = null; this.containsDot = false; } + this.symbolTable = symbolTable; } /** @@ -136,8 +156,6 @@ public AviatorObject div(final AviatorObject other, final Map en } } - - @Override public AviatorObject match(final AviatorObject other, final Map env) { Object val = getValue(env); @@ -354,7 +372,6 @@ public static Object getValueFromEnv(final String name, final boolean nameContai return null; } - @Override public AviatorObject defineValue(final AviatorObject value, final Map env) { if (this.containsDot) { diff --git a/src/main/java/com/googlecode/aviator/serialize/AviatorObjectInputStream.java b/src/main/java/com/googlecode/aviator/serialize/AviatorObjectInputStream.java index 2b2ed46f..9a1816d6 100644 --- a/src/main/java/com/googlecode/aviator/serialize/AviatorObjectInputStream.java +++ b/src/main/java/com/googlecode/aviator/serialize/AviatorObjectInputStream.java @@ -15,10 +15,12 @@ import com.googlecode.aviator.lexer.SymbolTable; import com.googlecode.aviator.lexer.token.Variable; import com.googlecode.aviator.parser.AviatorClassLoader; +import com.googlecode.aviator.runtime.LambdaFunctionBootstrap; 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.Env; import com.googlecode.aviator.utils.Reflector; /** @@ -47,14 +49,10 @@ protected Object resolveObject(Object obj) throws IOException { Object object = super.resolveObject(obj); if (object instanceof BaseExpression) { BaseExpression exp = (BaseExpression) object; - exp.setInstance(this.instance); - if (exp.getCompileEnv() != null) { - exp.getCompileEnv().setInstance(this.instance); - } - if (object instanceof ClassExpression) { - ((ClassExpression) object) - .setClassBytes(this.classBytesCache.get(object.getClass().getName())); - } + configureExpression(exp); + } + if (object instanceof Env) { + ((Env) object).setInstance(this.instance); } // Processing some internal constants. @@ -83,6 +81,13 @@ protected Object resolveObject(Object obj) throws IOException { return object; } + private void configureExpression(BaseExpression exp) { + exp.setInstance(this.instance); + if (exp instanceof ClassExpression) { + ((ClassExpression) exp).setClassBytes(this.classBytesCache.get(exp.getClass().getName())); + } + } + @Override protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { diff --git a/src/main/java/com/googlecode/aviator/serialize/AviatorObjectOutputStream.java b/src/main/java/com/googlecode/aviator/serialize/AviatorObjectOutputStream.java index 757710a8..3a5f2509 100644 --- a/src/main/java/com/googlecode/aviator/serialize/AviatorObjectOutputStream.java +++ b/src/main/java/com/googlecode/aviator/serialize/AviatorObjectOutputStream.java @@ -40,8 +40,8 @@ protected void annotateClass(Class cl) throws IOException { if (ClassExpression.class.isAssignableFrom(cl) && cl != ClassExpression.class) { byte[] classBytes = this.classBytesCache.get(cl.getName()); if (classBytes == null) { - throw new IllegalArgumentException( - "Class bytes not found, forgot to enable Options.SERIALIZABLE before compiling the script?"); + throw new IllegalArgumentException("Class bytes not found: " + cl.getName() + + ", forgot to enable Options.SERIALIZABLE before compiling the script?"); } this.writeInt(classBytes.length); this.write(classBytes); diff --git a/src/main/java/com/googlecode/aviator/utils/ArrayHashMap.java b/src/main/java/com/googlecode/aviator/utils/ArrayHashMap.java index 48cb5b5e..ab6b5bb1 100644 --- a/src/main/java/com/googlecode/aviator/utils/ArrayHashMap.java +++ b/src/main/java/com/googlecode/aviator/utils/ArrayHashMap.java @@ -13,7 +13,9 @@ public class ArrayHashMap extends AbstractMap private static final long serialVersionUID = 362498820763181265L; - private static class MapEntry implements Map.Entry { + private static class MapEntry implements Map.Entry, Serializable { + + private static final long serialVersionUID = 1759214536880718767L; K key; V value; int hash; diff --git a/src/main/java/com/googlecode/aviator/utils/Env.java b/src/main/java/com/googlecode/aviator/utils/Env.java index 88eb2276..d21342d0 100644 --- a/src/main/java/com/googlecode/aviator/utils/Env.java +++ b/src/main/java/com/googlecode/aviator/utils/Env.java @@ -28,10 +28,12 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Callable; import com.googlecode.aviator.AviatorEvaluatorInstance; import com.googlecode.aviator.Expression; import com.googlecode.aviator.Feature; @@ -296,6 +298,76 @@ public Set> entrySet() { return ret; } + static class TargetObjectTask implements GetValueTask { + + public TargetObjectTask(Object target) { + super(); + this.target = target; + } + + Object target; + + @Override + public Object call(Env env) { + return target; + } + + } + + static interface GetValueTask { + Object call(Env env); + } + + /** + * Internal variable tasks to get the value. + */ + private static final IdentityHashMap INTERNAL_VARIABLES = + new IdentityHashMap(); + + static { + INTERNAL_VARIABLES.put(Constants.REDUCER_LOOP_VAR, new TargetObjectTask(Range.LOOP)); + INTERNAL_VARIABLES.put(Constants.REDUCER_EMPTY_VAR, + new TargetObjectTask(ReducerResult.withEmpty(AviatorNil.NIL))); + INTERNAL_VARIABLES.put(Constants.ENV_VAR, new GetValueTask() { + + @Override + public Object call(Env env) { + env.instance.ensureFeatureEnabled(Feature.InternalVars); + return env; + } + + }); + INTERNAL_VARIABLES.put(Constants.FUNC_ARGS_VAR, new GetValueTask() { + + @Override + public Object call(Env env) { + env.instance.ensureFeatureEnabled(Feature.InternalVars); + return FunctionUtils.getFunctionArguments(env); + } + + }); + INTERNAL_VARIABLES.put(Constants.INSTANCE_VAR, new GetValueTask() { + + @Override + public Object call(Env env) { + env.instance.ensureFeatureEnabled(Feature.InternalVars); + return env.instance; + } + + }); + + INTERNAL_VARIABLES.put(Constants.EXP_VAR, new GetValueTask() { + + @Override + public Object call(Env env) { + env.instance.ensureFeatureEnabled(Feature.InternalVars); + return env.expression; + } + + }); + + } + /** * Get value for key. If the key is present in the overrides map, the value from that map is * returned; otherwise, the value for the key in the defaults map is returned. @@ -305,30 +377,9 @@ public Set> entrySet() { */ @Override public Object get(final Object key) { - // Should check ENV_VAR at first - // TODO: performance tweak - if (Constants.REDUCER_LOOP_VAR.equals(key)) { - return Range.LOOP; - } - if (Constants.REDUCER_EMPTY_VAR.equals(key)) { - return ReducerResult.withEmpty(AviatorNil.NIL); - } - - if (Constants.ENV_VAR.equals(key)) { - this.instance.ensureFeatureEnabled(Feature.InternalVars); - return this; - } - if (Constants.FUNC_ARGS_VAR.equals(key)) { - this.instance.ensureFeatureEnabled(Feature.InternalVars); - return FunctionUtils.getFunctionArguments(this); - } - if (Constants.INSTANCE_VAR.equals(key)) { - this.instance.ensureFeatureEnabled(Feature.InternalVars); - return this.instance; - } - if (Constants.EXP_VAR.equals(key)) { - this.instance.ensureFeatureEnabled(Feature.InternalVars); - return this.expression; + GetValueTask task = INTERNAL_VARIABLES.get(key); + if (task != null) { + return task.call(this); } Map overrides = getmOverrides(true); diff --git a/src/test/java/com/googlecode/aviator/scripts/TestScripts.java b/src/test/java/com/googlecode/aviator/scripts/TestScripts.java index 49ab1b16..092a987e 100644 --- a/src/test/java/com/googlecode/aviator/scripts/TestScripts.java +++ b/src/test/java/com/googlecode/aviator/scripts/TestScripts.java @@ -402,6 +402,7 @@ public void testFunctions() { assertEquals(610, testScript("fibonacci.av", "n", 15)); assertEquals(6765, testScript("fibonacci.av", "n", 20)); testScript("unpacking_arguments.av"); + assertEquals(Arrays.asList(3L, 2L, 4L, 1L), testScript("recusive_fn.av")); } @Test diff --git a/src/test/resources/scripts/recusive_fn.av b/src/test/resources/scripts/recusive_fn.av new file mode 100644 index 00000000..194ebdbd --- /dev/null +++ b/src/test/resources/scripts/recusive_fn.av @@ -0,0 +1,20 @@ + fn post_order(nodes, values) { + for node in nodes { + if (node.children != nil && !is_empty(node.children)) { + post_order(node.children, values); + } + seq.add(values, node.value); + } +} + +let tree = seq.list(seq.map( + "value", 1, + "children", seq.list(seq.map( + "value", 2, + "children", seq.list(seq.map( + "value", 3))), seq.map( + "value", 4)))); +let values = seq.list(); +post_order(tree, values); + +return values;