You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2022/05/05 22:22:27 UTC
[commons-jexl] branch master updated: JEXL-342: extend arithmetic to dereference references (sic);
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 <he...@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();