This is an automated email from the ASF dual-hosted git repository. henrib pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-jexl.git
The following commit(s) were added to refs/heads/master by this push: new fa3e3db7 JEXL-342: extend arithmetic to dereference references (sic); fa3e3db7 is described below commit fa3e3db789e85beb7bafcfa882af43a5ae5026c8 Author: henrib <hen...@apache.org> AuthorDate: Fri May 6 00:22:22 2022 +0200 JEXL-342: extend arithmetic to dereference references (sic); --- .../apache/commons/jexl3/internal/Interpreter.java | 17 +- .../commons/jexl3/jexl342/OptionalArithmetic.java | 231 +++++++++++++++++++++ .../apache/commons/jexl3/jexl342/OptionalTest.java | 52 +++++ 3 files changed, 293 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java index 469e184d..b3c1ed84 100644 --- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java +++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java @@ -114,7 +114,7 @@ public class Interpreter extends InterpreterBase { throw new JexlException.StackOverflow(node.jexlInfo(), "jexl (" + jexl.stackOverflow + ")", null); } cancelCheck(node); - return node.jjtAccept(this, null); + return arithmetic.controlReturn(node.jjtAccept(this, null)); } catch(final StackOverflowError xstack) { final JexlException xjexl = new JexlException.StackOverflow(node.jexlInfo(), "jvm", xstack); if (!isSilent()) { @@ -139,13 +139,16 @@ public class Interpreter extends InterpreterBase { logger.warn(xjexl.getMessage(), xjexl.getCause()); } } finally { - synchronized(this) { - if (functors != null) { - for (final Object functor : functors.values()) { - closeIfSupported(functor); + // clean functors at top level + if (fp == 0) { + synchronized (this) { + if (functors != null) { + for (final Object functor : functors.values()) { + closeIfSupported(functor); + } + functors.clear(); + functors = null; } - functors.clear(); - functors = null; } } jexl.putThreadEngine(tjexl); diff --git a/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java new file mode 100644 index 00000000..dcb11914 --- /dev/null +++ b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalArithmetic.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.jexl3.jexl342; + +import org.apache.commons.jexl3.JexlArithmetic; + +import java.lang.ref.Reference; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Unwraps Optional/Reference/AtomicReference on public and key methods. + */ +public class OptionalArithmetic extends JexlArithmetic { + public OptionalArithmetic(boolean astrict) { + super(astrict); + } + + /** + * Dereferences an Optional, a Reference or an AtomicReference, leave other as is. + * @param ref the reference + * @return the referenced object + */ + protected Object star(Object ref) { + if (ref instanceof Optional<?>) { + Optional<?> o = (Optional<?>) ref; + return o.orElse(null); + } + if (ref instanceof Reference<?>) { + Optional<?> r = (Optional<?>) ref; + return r.get(); + } + if (ref instanceof AtomicReference<?>) { + AtomicReference<?> r = (AtomicReference<?>) ref; + return r.get(); + } + return ref; + } + + @Override + public Object controlReturn(Object returned) { + return star(returned); + } + + @Override + public boolean toBoolean(final Object val) { + return super.toBoolean(star(val)); + } + + @Override + public String toString(final Object val) { + return super.toString(star(val)); + } + + @Override + public int toInteger(final Object val) { + return super.toInteger(star(val)); + } + + @Override + public long toLong(final Object val) { + return super.toLong(star(val)); + } + + @Override + public double toDouble(final Object val) { + return super.toDouble(star(val)); + } + + @Override + public BigInteger toBigInteger(final Object val) { + return super.toBigInteger(star(val)); + } + + @Override + public BigDecimal toBigDecimal(final Object val) { + return super.toBigDecimal(star(val)); + } + + @Override + public Integer size(final Object object, final Integer def) { + return super.size(star(object), def); + } + + @Override + public Boolean empty(Object o) { + return super.empty(star(o)); + } + + @Override + public Boolean isEmpty(final Object object, final Boolean def) { + return super.isEmpty(star(object), def); + } + + @Override + public Object positivize(Object o) { + return super.positivize(star(o)); + } + + @Override + public Object negate(Object o) { + return super.negate(star(o)); + } + + @Override + public Object complement(Object o) { + return super.complement(star(o)); + } + + @Override + public Boolean contains(Object lhs, Object rhs) { + return super.contains(star(lhs), star(rhs)); + } + + @Override + public Object add(Object lhs, Object rhs) { + return super.add(star(lhs), star(rhs)); + } + + @Override + public Object subtract(Object lhs, Object rhs) { + return super.subtract(star(lhs), star(rhs)); + } + + @Override + public Object multiply(Object lhs, Object rhs) { + return super.multiply(star(lhs), star(rhs)); + } + + @Override + public Object divide(Object lhs, Object rhs) { + return super.divide(star(lhs), star(rhs)); + } + + @Override + public Object mod(Object lhs, Object rhs) { + return super.mod(star(lhs), star(rhs)); + } + + @Override + public Object and(final Object left, final Object right) { return super.and(star(left), star(right)); } + + @Override + public Object or(final Object left, final Object right) { return super.or(star(left), star(right)); } + + @Override + public Object xor(final Object left, final Object right) { return super.xor(star(left), star(right)); } + + @Override + public Object shiftLeft(final Object left, final Object right) { return super.shiftLeft(star(left), star(right)); } + + @Override + public Object shiftRight(final Object left, final Object right) { return super.shiftRight(star(left), star(right)); } + + @Override + public Object shiftRightUnsigned(final Object left, final Object right) { return super.shiftRightUnsigned(star(left), star(right)); } + + @Override + public Boolean startsWith(final Object left, final Object right) { + return super.startsWith(star(left), star(right)); + } + + @Override + public Boolean endsWith(final Object left, final Object right) { + return super.endsWith(star(left), star(right)); + } + + @Override + public boolean equals(final Object left, final Object right) { + return equals(star(left), star(right)); + } + + @Override + public boolean greaterThan(final Object left, final Object right) { + return greaterThan(star(left), star(right)); + } + + @Override + public boolean greaterThanOrEqual(final Object left, final Object right) { + return greaterThanOrEqual(star(left), star(right)); + } + + @Override + public boolean lessThan(final Object left, final Object right) { + return lessThan(star(left), star(right)); + } + + @Override + public boolean lessThanOrEqual(final Object left, final Object right) { + return lessThanOrEqual(star(left), star(right)); + } + + @Override + public boolean narrowArguments(final Object[] args) { + boolean narrowed = false; + if (args != null) { + for (int a = 0; a < args.length; ++a) { + final Object arg = args[a]; + Object sarg = star(arg); + if (sarg != arg) { + narrowed = true; + } + if (arg instanceof Number) { + final Number narg = (Number) arg; + final Number narrow = narrow(narg); + if (!narg.equals(narrow)) { + args[a] = narrow; + narrowed = true; + } + } + } + } + return narrowed; + } +} diff --git a/src/test/java/org/apache/commons/jexl3/jexl342/OptionalTest.java b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalTest.java index ea28e115..068cce98 100644 --- a/src/test/java/org/apache/commons/jexl3/jexl342/OptionalTest.java +++ b/src/test/java/org/apache/commons/jexl3/jexl342/OptionalTest.java @@ -16,18 +16,26 @@ */ package org.apache.commons.jexl3.jexl342; +import org.apache.commons.jexl3.JexlArithmetic; import org.apache.commons.jexl3.JexlBuilder; +import org.apache.commons.jexl3.JexlContext; import org.apache.commons.jexl3.JexlEngine; import org.apache.commons.jexl3.JexlException; +import org.apache.commons.jexl3.JexlFeatures; import org.apache.commons.jexl3.JexlInfo; import org.apache.commons.jexl3.JexlScript; +import org.apache.commons.jexl3.MapContext; import org.apache.commons.jexl3.introspection.JexlUberspect; import org.junit.Assert; import org.junit.Test; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; public class OptionalTest { @@ -45,6 +53,50 @@ public class OptionalTest { } } + public static class StreamContext extends MapContext { + public Stream map(Collection<Object> c, JexlScript s) { + JexlContext context = JexlEngine.getThreadContext(); + return c.stream().map(a->s.execute(context, a)); + } + public Object reduce(Stream<Object> stream, JexlScript script) { + return stream.reduce((identity, element)->{ + JexlContext context = JexlEngine.getThreadContext(); + return script.execute(context, identity, element); + }); + } + } + + @Test + public void testStream() { + String src = "[1, 2, 3, ...].map(x -> x * x).reduce((acc, x)->acc + x)"; + JexlBuilder builder = new JexlBuilder(); + JexlUberspect uber = builder.create().getUberspect(); + JexlArithmetic jexla = new OptionalArithmetic(true); + JexlEngine jexl = builder.uberspect(new ReferenceUberspect(uber)).arithmetic(jexla).safe(false).create(); + JexlInfo info = new JexlInfo("testStream", 1, 1); + MapContext context = new StreamContext(); + JexlScript script = jexl.createScript(src, "list"); + Object result = script.execute(context, Arrays.asList(1, 2, 3)); + Assert.assertEquals(14, result); + //Optional<?> result = (Optional<?>) script.execute(context, Arrays.asList(1, 2, 3)); + //Assert.assertEquals(14, result.get()); + } + + @Test + public void testOptionalArgs() { + JexlBuilder builder = new JexlBuilder(); + JexlArithmetic jexla = new OptionalArithmetic(true); + JexlUberspect uber = builder.create().getUberspect(); + JexlEngine jexl = builder.uberspect(new ReferenceUberspect(uber)).arithmetic(jexla).safe(false).create(); + JexlInfo info = new JexlInfo("testStream", 1, 1); + MapContext context = new StreamContext(); + String src = "x + x"; + JexlScript script = jexl.createScript(src, "x"); + Optional<Integer> x = Optional.of(21); + Object result = script.execute(context, x); + Assert.assertEquals(42, result); + } + @Test public void test342() { JexlBuilder builder = new JexlBuilder();