From 4dd218b93d945bdea4043683a6181e3c2fa43448 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Wed, 24 Dec 2025 09:47:20 +0100 Subject: [PATCH] Improve coercion performance Move LambdaExpression coercion further down Reduce method complexity to allow better inlining. When the coerce method has too many conditions it cannot be properly inlined, at least not on Java 25 Temurin --- .../odysseus/el/misc/TypeConverterImpl.java | 129 +++++++++++++----- 1 file changed, 97 insertions(+), 32 deletions(-) diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/misc/TypeConverterImpl.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/misc/TypeConverterImpl.java index c7b9bf5499e..c08ddc3689d 100644 --- a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/misc/TypeConverterImpl.java +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/de/odysseus/el/misc/TypeConverterImpl.java @@ -43,6 +43,9 @@ protected Boolean coerceToBoolean(Object value) { if (value instanceof String) { return Boolean.valueOf((String)value); } + if (value instanceof LambdaExpression lambdaExpression) { + return coerceToBoolean(resolveLambdaExpression(lambdaExpression)); + } throw new ELException(LocalMessages.get("error.coerce.type", value, value.getClass(), Boolean.class)); } @@ -59,6 +62,9 @@ protected Character coerceToCharacter(Object value) { if (value instanceof String) { return Character.valueOf(((String)value).charAt(0)); } + if (value instanceof LambdaExpression lambdaExpression) { + return coerceToCharacter(resolveLambdaExpression(lambdaExpression)); + } throw new ELException(LocalMessages.get("error.coerce.type", value, value.getClass(), Character.class)); } @@ -85,6 +91,9 @@ protected BigDecimal coerceToBigDecimal(Object value) { if (value instanceof Character) { return new BigDecimal((short)((Character)value).charValue()); } + if (value instanceof LambdaExpression lambdaExpression) { + return coerceToBigDecimal(resolveLambdaExpression(lambdaExpression)); + } throw new ELException(LocalMessages.get("error.coerce.type", value, value.getClass(), BigDecimal.class)); } @@ -111,6 +120,9 @@ protected BigInteger coerceToBigInteger(Object value) { if (value instanceof Character) { return BigInteger.valueOf((short)((Character)value).charValue()); } + if (value instanceof LambdaExpression lambdaExpression) { + return coerceToBigInteger(resolveLambdaExpression(lambdaExpression)); + } throw new ELException(LocalMessages.get("error.coerce.type", value, value.getClass(), BigInteger.class)); } @@ -134,6 +146,9 @@ protected Double coerceToDouble(Object value) { if (value instanceof Character) { return Double.valueOf((short)((Character)value).charValue()); } + if (value instanceof LambdaExpression lambdaExpression) { + return coerceToDouble(resolveLambdaExpression(lambdaExpression)); + } throw new ELException(LocalMessages.get("error.coerce.type", value, value.getClass(), Double.class)); } @@ -157,6 +172,9 @@ protected Float coerceToFloat(Object value) { if (value instanceof Character) { return Float.valueOf((short)((Character)value).charValue()); } + if (value instanceof LambdaExpression lambdaExpression) { + return coerceToFloat(resolveLambdaExpression(lambdaExpression)); + } throw new ELException(LocalMessages.get("error.coerce.type", value, value.getClass(), Float.class)); } @@ -180,6 +198,9 @@ protected Long coerceToLong(Object value) { if (value instanceof Character) { return Long.valueOf((short)((Character)value).charValue()); } + if (value instanceof LambdaExpression lambdaExpression) { + return coerceToLong(resolveLambdaExpression(lambdaExpression)); + } throw new ELException(LocalMessages.get("error.coerce.type", value, value.getClass(), Long.class)); } @@ -203,6 +224,9 @@ protected Integer coerceToInteger(Object value) { if (value instanceof Character) { return Integer.valueOf((short)((Character)value).charValue()); } + if (value instanceof LambdaExpression lambdaExpression) { + return coerceToInteger(resolveLambdaExpression(lambdaExpression)); + } throw new ELException(LocalMessages.get("error.coerce.type", value, value.getClass(), Integer.class)); } @@ -226,6 +250,9 @@ protected Short coerceToShort(Object value) { if (value instanceof Character) { return Short.valueOf((short)((Character)value).charValue()); } + if (value instanceof LambdaExpression lambdaExpression) { + return coerceToShort(resolveLambdaExpression(lambdaExpression)); + } throw new ELException(LocalMessages.get("error.coerce.type", value, value.getClass(), Short.class)); } @@ -249,6 +276,9 @@ protected Byte coerceToByte(Object value) { if (value instanceof Character) { return Byte.valueOf(Short.valueOf((short)((Character)value).charValue()).byteValue()); } + if (value instanceof LambdaExpression lambdaExpression) { + return coerceToByte(resolveLambdaExpression(lambdaExpression)); + } throw new ELException(LocalMessages.get("error.coerce.type", value, value.getClass(), Byte.class)); } @@ -262,9 +292,20 @@ protected String coerceToString(Object value) { if (value instanceof Enum) { return ((Enum)value).name(); } + if (value instanceof LambdaExpression lambdaExpression) { + return coerceToString(resolveLambdaExpression(lambdaExpression)); + } return value.toString(); } + protected Object resolveLambdaExpression(LambdaExpression lambdaExpression) { + Object value = lambdaExpression; + while (value instanceof LambdaExpression expression && expression.getFormalParameters().isEmpty()) { + value = expression.invoke(); + } + return value; + } + @SuppressWarnings("unchecked") protected > T coerceToEnum(Object value, Class type) { if (value == null || "".equals(value)) { @@ -280,6 +321,9 @@ protected > T coerceToEnum(Object value, Class type) { throw new ELException(LocalMessages.get("error.coerce.value", value, value.getClass(), type), e); } } + if (value instanceof LambdaExpression lambdaExpression) { + return coerceToEnum(resolveLambdaExpression(lambdaExpression), type); + } throw new ELException(LocalMessages.get("error.coerce.type", value, value.getClass(), type)); } @@ -325,59 +369,68 @@ protected T coerceToFunctionalInterface(LambdaExpression lambdaExpression, C return proxy.get(); } - @SuppressWarnings("unchecked") - protected Object coerceToType(Object value, Class type) { - if (type == null) { - return value; - } - - if (value instanceof LambdaExpression lambdaExpression) { - if (LambdaExpression.class == type) { - return lambdaExpression; - } - if (isFunctionalInterface(type)) { - return coerceToFunctionalInterface(lambdaExpression, (Class) type); - } - - if (lambdaExpression.getFormalParameters().isEmpty()) { - // If the value is a LambdaExpression without formal parameters we need to resolve its value - value = coerceToType(lambdaExpression.invoke(), type); - } - } - - if (Object.class.equals(type)) { - return value; - } + protected Object coerceToPrimitive(Object value, Class type) { + if (type == long.class) { + return coerceToLong(value); + } + if (type == double.class) { + return coerceToDouble(value); + } + if (type == boolean.class) { + return coerceToBoolean(value); + } + if (type == int.class) { + return coerceToInteger(value); + } + if (type == float.class) { + return coerceToFloat(value); + } + if (type == short.class) { + return coerceToShort(value); + } + if (type == byte.class) { + return coerceToByte(value); + } + if (type == char.class) { + return coerceToCharacter(value); + } + throw new ELException(LocalMessages.get("error.coerce.type", value, value.getClass(), type)); + } + @SuppressWarnings("unchecked") + protected Object coerceToType(Object value, Class type) { if (type == String.class) { return coerceToString(value); } - if (value == null && !type.isPrimitive()) { + if (type.isPrimitive()) { + return coerceToPrimitive(value, type); + } + if (value == null) { return null; } - if (type == Long.class || type == long.class) { + if (type == Long.class) { return coerceToLong(value); } - if (type == Double.class || type == double.class) { + if (type == Double.class) { return coerceToDouble(value); } - if (type == Boolean.class || type == boolean.class) { + if (type == Boolean.class) { return coerceToBoolean(value); } - if (type == Integer.class || type == int.class) { + if (type == Integer.class) { return coerceToInteger(value); } - if (type == Float.class || type == float.class) { + if (type == Float.class) { return coerceToFloat(value); } - if (type == Short.class || type == short.class) { + if (type == Short.class) { return coerceToShort(value); } - if (type == Byte.class || type == byte.class) { + if (type == Byte.class) { return coerceToByte(value); } - if (type == Character.class || type == char.class) { + if (type == Character.class) { return coerceToCharacter(value); } if (type == BigDecimal.class) { @@ -389,9 +442,21 @@ protected Object coerceToType(Object value, Class type) { if (type.getSuperclass() == Enum.class) { return coerceToEnum(value, (Class)type); } + if (value instanceof LambdaExpression lambdaExpression) { + if (LambdaExpression.class == type) { + return lambdaExpression; + } + + if (isFunctionalInterface(type)) { + return coerceToFunctionalInterface(lambdaExpression, (Class) type); + } + + value = resolveLambdaExpression(lambdaExpression); + } if (value == null || value.getClass() == type || type.isInstance(value)) { return value; } + if (value instanceof String) { return coerceStringToType((String)value, type); }