You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2021/01/22 12:51:29 UTC

[tomcat] branch 8.5.x updated (01c08ff -> 616949e)

This is an automated email from the ASF dual-hosted git repository.

markt pushed a change to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git.


    from 01c08ff  New StringInterpreter to allow optimised String -> Type conversions
     new faa236f  BZ 64872 Optimised ELInterpreter for literal booleans & strings
     new 616949e  BZ 64872 Optimised StringInterpreter for Enum Values

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../optimizations/ELInterpreterTagSetters.java     | 268 ++++++++++
 .../optimizations/StringInterpreterEnum.java}      |  30 +-
 .../optimizations/TestELInterpreterTagSetters.java | 550 +++++++++++++++++++++
 .../TestStringInterpreterTagSetters.java           | 120 +++++
 test/webapp/WEB-INF/tag-setters.tld                | 245 +++++++++
 .../bug64872-bigdecimal.jsp}                       |  16 +-
 .../bug64872-biginteger.jsp}                       |  16 +-
 .../bug56147.jsp => bug6nnnn/bug64872-boolean.jsp} |  28 +-
 .../{bug46381.jsp => bug6nnnn/bug64872-byte.jsp}   |  18 +-
 .../bug64872-character.jsp}                        |  16 +-
 .../{bug46381.jsp => bug6nnnn/bug64872-double.jsp} |  16 +-
 .../{bug46381.jsp => bug6nnnn/bug64872-float.jsp}  |  16 +-
 .../bug64872-integer.jsp}                          |  16 +-
 .../{bug46381.jsp => bug6nnnn/bug64872-long.jsp}   |  16 +-
 .../bug64872-primitive-boolean.jsp}                |  28 +-
 .../bug64872-primitive-byte.jsp}                   |  18 +-
 .../bug64872-primitive-character.jsp}              |  16 +-
 .../bug64872-primitive-double.jsp}                 |  16 +-
 .../bug64872-primitive-float.jsp}                  |  16 +-
 .../bug64872-primitive-integer.jsp}                |  16 +-
 .../bug64872-primitive-long.jsp}                   |  16 +-
 .../bug64872-primitive-short.jsp}                  |  16 +-
 .../{bug46381.jsp => bug6nnnn/bug64872-short.jsp}  |  16 +-
 .../{bug46381.jsp => bug6nnnn/bug64872-string.jsp} |  19 +-
 .../bug64872-timeunit.jsp}                         |  16 +-
 .../bug64872b-timeunit.jsp}                        |  13 +-
 webapps/docs/jasper-howto.xml                      |  14 +-
 27 files changed, 1419 insertions(+), 172 deletions(-)
 create mode 100644 java/org/apache/jasper/optimizations/ELInterpreterTagSetters.java
 copy java/{javax/el/Expression.java => org/apache/jasper/optimizations/StringInterpreterEnum.java} (58%)
 create mode 100644 test/org/apache/jasper/optimizations/TestELInterpreterTagSetters.java
 create mode 100644 test/org/apache/jasper/optimizations/TestStringInterpreterTagSetters.java
 create mode 100644 test/webapp/WEB-INF/tag-setters.tld
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-bigdecimal.jsp} (71%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-biginteger.jsp} (71%)
 copy test/webapp/{bug5nnnn/bug56147.jsp => bug6nnnn/bug64872-boolean.jsp} (54%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-byte.jsp} (65%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-character.jsp} (71%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-double.jsp} (71%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-float.jsp} (72%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-integer.jsp} (71%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-long.jsp} (72%)
 copy test/webapp/{bug5nnnn/bug56147.jsp => bug6nnnn/bug64872-primitive-boolean.jsp} (52%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-primitive-byte.jsp} (63%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-primitive-character.jsp} (70%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-primitive-double.jsp} (70%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-primitive-float.jsp} (70%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-primitive-integer.jsp} (70%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-primitive-long.jsp} (71%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-primitive-short.jsp} (71%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-short.jsp} (72%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-string.jsp} (67%)
 copy test/webapp/{bug46381.jsp => bug6nnnn/bug64872-timeunit.jsp} (71%)
 copy test/webapp/{bug5nnnn/bug51544.jsp => bug6nnnn/bug64872b-timeunit.jsp} (76%)


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 01/02: BZ 64872 Optimised ELInterpreter for literal booleans & strings

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit faa236fab5f93f8bbb93dd20e6dfd25907cd2ebb
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Mon Nov 16 17:27:27 2020 +0000

    BZ 64872 Optimised ELInterpreter for literal booleans & strings
    
    This is not strictly spec compliant hence it isn't enabled by
    default but it should work for many applicaitons.
    https://bz.apache.org/bugzilla/show_bug.cgi?id=64872
---
 .../optimizations/ELInterpreterTagSetters.java     | 268 ++++++++++
 .../optimizations/TestELInterpreterTagSetters.java | 547 +++++++++++++++++++++
 test/webapp/WEB-INF/tag-setters.tld                | 245 +++++++++
 test/webapp/bug6nnnn/bug64872-bigdecimal.jsp       |  30 ++
 test/webapp/bug6nnnn/bug64872-biginteger.jsp       |  30 ++
 test/webapp/bug6nnnn/bug64872-boolean.jsp          |  37 ++
 test/webapp/bug6nnnn/bug64872-byte.jsp             |  32 ++
 test/webapp/bug6nnnn/bug64872-character.jsp        |  30 ++
 test/webapp/bug6nnnn/bug64872-double.jsp           |  30 ++
 test/webapp/bug6nnnn/bug64872-float.jsp            |  30 ++
 test/webapp/bug6nnnn/bug64872-integer.jsp          |  30 ++
 test/webapp/bug6nnnn/bug64872-long.jsp             |  30 ++
 .../webapp/bug6nnnn/bug64872-primitive-boolean.jsp |  37 ++
 test/webapp/bug6nnnn/bug64872-primitive-byte.jsp   |  32 ++
 .../bug6nnnn/bug64872-primitive-character.jsp      |  30 ++
 test/webapp/bug6nnnn/bug64872-primitive-double.jsp |  30 ++
 test/webapp/bug6nnnn/bug64872-primitive-float.jsp  |  30 ++
 .../webapp/bug6nnnn/bug64872-primitive-integer.jsp |  30 ++
 test/webapp/bug6nnnn/bug64872-primitive-long.jsp   |  30 ++
 test/webapp/bug6nnnn/bug64872-primitive-short.jsp  |  30 ++
 test/webapp/bug6nnnn/bug64872-short.jsp            |  30 ++
 test/webapp/bug6nnnn/bug64872-string.jsp           |  33 ++
 test/webapp/bug6nnnn/bug64872-timeunit.jsp         |  33 ++
 webapps/docs/jasper-howto.xml                      |   7 +-
 24 files changed, 1690 insertions(+), 1 deletion(-)

diff --git a/java/org/apache/jasper/optimizations/ELInterpreterTagSetters.java b/java/org/apache/jasper/optimizations/ELInterpreterTagSetters.java
new file mode 100644
index 0000000..38feb96
--- /dev/null
+++ b/java/org/apache/jasper/optimizations/ELInterpreterTagSetters.java
@@ -0,0 +1,268 @@
+/*
+ * 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.jasper.optimizations;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.el.ELResolver;
+
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.compiler.ELInterpreter;
+import org.apache.jasper.compiler.JspUtil;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+/**
+ * A non-specification compliant {@link ELInterpreter} that optimizes a subset
+ * of setters for tag attributes.
+ * <p>
+ * The cases optimized by this implementation are:
+ * <ul>
+ * <li>expressions that are solely a literal boolean</li>
+ * <li>expressions that are solely a constant string used (with coercion where
+ *     necessary) with a setter that accepts:</li>
+ *     <ul>
+ *     <li>boolean / Boolean</li>
+ *     <li>char / Character</li>
+ *     <li>BigDecimal</li>
+ *     <li>long / Long</li>
+ *     <li>int / Integer</li>
+ *     <li>short / Short</li>
+ *     <li>byte / Byte</li>
+ *     <li>double / Double</li>
+ *     <li>float / Float</li>
+ *     <li>BigInteger</li>
+ *     <li>Enum</li>
+ *     <li>String</li>
+ *     </ul>
+ * </ul>
+ * The specification compliance issue is that it essentially skips the first
+ * three {@link ELResolver}s listed in section JSP.2.9 and effectively hard
+ * codes the use of the 4th {@link ELResolver} in that list.
+ *
+ * @see "https://bz.apache.org/bugzilla/show_bug.cgi?id=64872"
+ */
+public class ELInterpreterTagSetters implements ELInterpreter {
+
+    // Can't be static
+    private final Log log = LogFactory.getLog(ELInterpreterTagSetters.class);
+
+    private final Pattern PATTERN_BOOLEAN = Pattern.compile("[$][{]([\"']?)(true|false)\\1[}]");
+    private final Pattern PATTERN_STRING_CONSTANT = Pattern.compile("[$][{]([\"'])(\\w+)\\1[}]");
+    private final Pattern PATTERN_NUMERIC = Pattern.compile("[$][{]([\"'])([+-]?\\d+(\\.\\d+)?)\\1[}]");
+
+    @Override
+    public String interpreterCall(JspCompilationContext context,
+            boolean isTagFile, String expression,
+            Class<?> expectedType, String fnmapvar) {
+
+        String result = null;
+
+        // Boolean
+        if (Boolean.TYPE == expectedType) {
+            Matcher m = PATTERN_BOOLEAN.matcher(expression);
+            if (m.matches()) {
+                result = m.group(2);
+            }
+        } else if (Boolean.class == expectedType) {
+            Matcher m = PATTERN_BOOLEAN.matcher(expression);
+            if (m.matches()) {
+                if ("true".equals(m.group(2))) {
+                    result = "Boolean.TRUE";
+                } else {
+                    result = "Boolean.FALSE";
+                }
+            }
+        // Character
+        } else if (Character.TYPE == expectedType) {
+            Matcher m = PATTERN_STRING_CONSTANT.matcher(expression);
+            if (m.matches()) {
+                return "\'" + m.group(2).charAt(0) + "\'";
+            }
+        } else if (Character.class == expectedType) {
+            Matcher m = PATTERN_STRING_CONSTANT.matcher(expression);
+            if (m.matches()) {
+                return "Character.valueOf(\'" + m.group(2).charAt(0) + "\')";
+            }
+        // Numeric - BigDecimal
+        } else if (BigDecimal.class == expectedType) {
+            Matcher m = PATTERN_NUMERIC.matcher(expression);
+            if (m.matches()) {
+                try {
+                    @SuppressWarnings("unused")
+                    BigDecimal unused = new BigDecimal(m.group(2));
+                    result = "new java.math.BigDecimal(\"" + m.group(2) + "\")";
+                } catch (NumberFormatException e) {
+                    log.debug("Failed to convert [" + m.group(2) + "] to BigDecimal", e);
+                    // Continue and resolve the value at runtime
+                }
+            }
+        // Numeric - long/Long
+        } else if (Long.TYPE == expectedType || Long.class == expectedType) {
+            Matcher m = PATTERN_NUMERIC.matcher(expression);
+            if (m.matches()) {
+                try {
+                    @SuppressWarnings("unused")
+                    Long unused = Long.valueOf(m.group(2));
+                    if (expectedType.isPrimitive()) {
+                        // Long requires explicit declaration as a long literal
+                        result = m.group(2) + "L";
+                    } else {
+                        result = "Long.valueOf(\"" + m.group(2) + "\")";
+                    }
+                } catch (NumberFormatException e) {
+                    log.debug("Failed to convert [" + m.group(2) + "] to Long", e);
+                    // Continue and resolve the value at runtime
+                }
+            }
+        // Numeric - int/Integer
+        } else if (Integer.TYPE == expectedType || Integer.class == expectedType) {
+            Matcher m = PATTERN_NUMERIC.matcher(expression);
+            if (m.matches()) {
+                try {
+                    @SuppressWarnings("unused")
+                    Integer unused = Integer.valueOf(m.group(2));
+                    if (expectedType.isPrimitive()) {
+                        result = m.group(2);
+                    } else {
+                        result = "Integer.valueOf(\"" + m.group(2) + "\")";
+                    }
+                } catch (NumberFormatException e) {
+                    log.debug("Failed to convert [" + m.group(2) + "] to Integer", e);
+                    // Continue and resolve the value at runtime
+                }
+            }
+        // Numeric - short/Short
+        } else if (Short.TYPE == expectedType || Short.class == expectedType) {
+            Matcher m = PATTERN_NUMERIC.matcher(expression);
+            if (m.matches()) {
+                try {
+                    @SuppressWarnings("unused")
+                    Short unused = Short.valueOf(m.group(2));
+                    if (expectedType.isPrimitive()) {
+                        // short requires a downcast
+                        result = "(short) " + m.group(2);
+                    } else {
+                        result = "Short.valueOf(\"" + m.group(2) + "\")";
+                    }
+                } catch (NumberFormatException e) {
+                    log.debug("Failed to convert [" + m.group(2) + "] to Short", e);
+                    // Continue and resolve the value at runtime
+                }
+            }
+        // Numeric - byte/Byte
+        } else if (Byte.TYPE == expectedType || Byte.class == expectedType) {
+            Matcher m = PATTERN_NUMERIC.matcher(expression);
+            if (m.matches()) {
+                try {
+                    @SuppressWarnings("unused")
+                    Byte unused = Byte.valueOf(m.group(2));
+                    if (expectedType.isPrimitive()) {
+                        // byte requires a downcast
+                        result = "(byte) " + m.group(2);
+                    } else {
+                        result = "Byte.valueOf(\"" + m.group(2) + "\")";
+                    }
+                } catch (NumberFormatException e) {
+                    log.debug("Failed to convert [" + m.group(2) + "] to Byte", e);
+                    // Continue and resolve the value at runtime
+                }
+            }
+        // Numeric - double/Double
+        } else if (Double.TYPE == expectedType || Double.class == expectedType) {
+            Matcher m = PATTERN_NUMERIC.matcher(expression);
+            if (m.matches()) {
+                try {
+                    @SuppressWarnings("unused")
+                    Double unused = Double.valueOf(m.group(2));
+                    if (expectedType.isPrimitive()) {
+                        result = m.group(2);
+                    } else {
+                        result = "Double.valueOf(\"" + m.group(2) + "\")";
+                    }
+                } catch (NumberFormatException e) {
+                    log.debug("Failed to convert [" + m.group(2) + "] to Double", e);
+                    // Continue and resolve the value at runtime
+                }
+            }
+        // Numeric - float/Float
+        } else if (Float.TYPE == expectedType || Float.class == expectedType) {
+            Matcher m = PATTERN_NUMERIC.matcher(expression);
+            if (m.matches()) {
+                try {
+                    @SuppressWarnings("unused")
+                    Float unused = Float.valueOf(m.group(2));
+                    if (expectedType.isPrimitive()) {
+                        // Float requires explicit declaration as a float literal
+                        result = m.group(2) + "f";
+                    } else {
+                        result = "Float.valueOf(\"" + m.group(2) + "\")";
+                    }
+                } catch (NumberFormatException e) {
+                    log.debug("Failed to convert [" + m.group(2) + "] to Float", e);
+                    // Continue and resolve the value at runtime
+                }
+            }
+        // Numeric - BigInteger
+        } else if (BigInteger.class == expectedType) {
+            Matcher m = PATTERN_NUMERIC.matcher(expression);
+            if (m.matches()) {
+                try {
+                    @SuppressWarnings("unused")
+                    BigInteger unused = new BigInteger(m.group(2));
+                    result = "new java.math.BigInteger(\"" + m.group(2) + "\")";
+                } catch (NumberFormatException e) {
+                    log.debug("Failed to convert [" + m.group(2) + "] to BigInteger", e);
+                    // Continue and resolve the value at runtime
+                }
+            }
+        // Enum
+        } else if (expectedType.isEnum()){
+            Matcher m = PATTERN_STRING_CONSTANT.matcher(expression);
+            if (m.matches()) {
+                try {
+                    @SuppressWarnings({ "unchecked", "rawtypes" })
+                    Enum<?> enumValue = Enum.valueOf((Class<? extends Enum>) expectedType, m.group(2));
+                    result = expectedType.getName() + "." + enumValue.name();
+                } catch (IllegalArgumentException iae) {
+                    log.debug("Failed to convert [" + m.group(2) + "] to Enum type [" + expectedType.getName() + "]", iae);
+                    // Continue and resolve the value at runtime
+                }
+            }
+        // String
+        } else if (String.class == expectedType) {
+            Matcher m = PATTERN_STRING_CONSTANT.matcher(expression);
+            if (m.matches()) {
+                result = "\"" + m.group(2) + "\"";
+            }
+        }
+
+        if (result == null) {
+            result = JspUtil.interpreterCall(isTagFile, expression, expectedType,
+                    fnmapvar);
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("Expression [" + expression + "], type [" + expectedType.getName() + "], returns [" + result + "]");
+        }
+
+        return result;
+    }
+}
diff --git a/test/org/apache/jasper/optimizations/TestELInterpreterTagSetters.java b/test/org/apache/jasper/optimizations/TestELInterpreterTagSetters.java
new file mode 100644
index 0000000..5a6cf8f
--- /dev/null
+++ b/test/org/apache/jasper/optimizations/TestELInterpreterTagSetters.java
@@ -0,0 +1,547 @@
+/*
+ * 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.jasper.optimizations;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.SimpleTagSupport;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.compiler.ELInterpreter;
+import org.apache.jasper.compiler.ELInterpreterFactory;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+@RunWith(Parameterized.class)
+public class TestELInterpreterTagSetters extends TomcatBaseTest {
+
+    private static final Integer ZERO = Integer.valueOf(0);
+    private static final Integer TWO = Integer.valueOf(2);
+    private static final Integer THREE = Integer.valueOf(3);
+
+    @Parameters(name="{index}: {0} {1} {3},{4}")
+    public static Collection<Object[]> parameters() {
+        List<Object[]> parameterSets = new ArrayList<>();
+
+        ELInterpreter[] elInterpreters = new ELInterpreter[] {
+                // Warm-up
+                // First call will trigger compilation (and therefore be slower)
+                // Call both to ensure both are warmed up
+                new ELInterpreterWrapper(true, "TagSetters"),
+                new ELInterpreterWrapper(false, "Default"),
+                // Compare times of these test runs
+                new ELInterpreterWrapper(true, "TagSetters"),
+                new ELInterpreterWrapper(false, "Default"),
+                };
+
+        for (ELInterpreter elInterpreter : elInterpreters) {
+            parameterSets.add(new Object[] { elInterpreter, "boolean", "false", ZERO, THREE });
+            parameterSets.add(new Object[] { elInterpreter, "boolean", "true", THREE, THREE });
+            parameterSets.add(new Object[] { elInterpreter, "primitive-boolean", "false", ZERO, THREE });
+            parameterSets.add(new Object[] { elInterpreter, "primitive-boolean", "true", THREE, THREE });
+            parameterSets.add(new Object[] { elInterpreter, "character", "b", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "primitive-character", "b", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "bigdecimal", "12.34", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "biginteger", "1234", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "long", "1234", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "primitive-long", "1234", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "integer", "1234", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "primitive-integer", "1234", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "short", "1234", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "primitive-short", "1234", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "byte", "12", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "byte", "-12", TWO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "primitive-byte", "12", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "primitive-byte", "-12", TWO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "double", "12.34", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "primitive-double", "12.34", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "float", "12.34", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "primitive-float", "12.34", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "timeunit", "SECONDS", ZERO, TWO });
+            parameterSets.add(new Object[] { elInterpreter, "string", "bar", ZERO, TWO });
+        }
+        return parameterSets;
+    }
+
+
+    @Parameter(0)
+    public ELInterpreter elInterpreter;
+    @Parameter(1)
+    public String target;
+    @Parameter(2)
+    public String expectedValue;
+    @Parameter(3)
+    public int offset;
+    @Parameter(4)
+    public int len;
+
+
+    @Test
+    public void testTag() throws Exception {
+        Tomcat tomcat = getTomcatInstanceTestWebapp(false, true);
+        Context ctxt = (Context) tomcat.getHost().findChild("/test");
+        ctxt.getServletContext().setAttribute(ELInterpreter.class.getCanonicalName(), elInterpreter);
+
+        ByteChunk bc = getUrl("http://localhost:" + getPort() + "/test/bug6nnnn/bug64872-" + target + ".jsp");
+
+        String actual = bc.toString();
+
+        for (int i = offset; i < offset + len; i++) {
+            String expected = String.format("%02d The value of foo is [%s]", Integer.valueOf(i+1), expectedValue);
+            Assert.assertTrue(actual, actual.contains(expected));
+        }
+    }
+
+
+    public static class TagPrimitiveBoolean extends SimpleTagSupport {
+
+        private boolean foo;
+
+        public boolean getFoo() {
+            return foo;
+        }
+
+        public void setFoo(boolean foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagBoolean extends SimpleTagSupport {
+
+        private Boolean foo;
+
+        public Boolean getFoo() {
+            return foo;
+        }
+
+        public void setFoo(Boolean foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagPrimitiveCharacter extends SimpleTagSupport {
+
+        private char foo;
+
+        public char getFoo() {
+            return foo;
+        }
+
+        public void setFoo(char foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagCharacter extends SimpleTagSupport {
+
+        private Character foo;
+
+        public Character getFoo() {
+            return foo;
+        }
+
+        public void setFoo(Character foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagPrimitiveLong extends SimpleTagSupport {
+
+        private long foo;
+
+        public long getFoo() {
+            return foo;
+        }
+
+        public void setFoo(long foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagLong extends SimpleTagSupport {
+
+        private Long foo;
+
+        public Long getFoo() {
+            return foo;
+        }
+
+        public void setFoo(Long foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagPrimitiveInteger extends SimpleTagSupport {
+
+        private int foo;
+
+        public int getFoo() {
+            return foo;
+        }
+
+        public void setFoo(int foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagInteger extends SimpleTagSupport {
+
+        private Integer foo;
+
+        public Integer getFoo() {
+            return foo;
+        }
+
+        public void setFoo(Integer foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagPrimitiveShort extends SimpleTagSupport {
+
+        private short foo;
+
+        public short getFoo() {
+            return foo;
+        }
+
+        public void setFoo(short foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagShort extends SimpleTagSupport {
+
+        private Short foo;
+
+        public Short getFoo() {
+            return foo;
+        }
+
+        public void setFoo(Short foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagPrimitiveByte extends SimpleTagSupport {
+
+        private byte foo;
+
+        public byte getFoo() {
+            return foo;
+        }
+
+        public void setFoo(byte foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagByte extends SimpleTagSupport {
+
+        private Byte foo;
+
+        public Byte getFoo() {
+            return foo;
+        }
+
+        public void setFoo(Byte foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagPrimitiveDouble extends SimpleTagSupport {
+
+        private double foo;
+
+        public double getFoo() {
+            return foo;
+        }
+
+        public void setFoo(double foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagDouble extends SimpleTagSupport {
+
+        private Double foo;
+
+        public Double getFoo() {
+            return foo;
+        }
+
+        public void setFoo(Double foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagPrimitiveFloat extends SimpleTagSupport {
+
+        private float foo;
+
+        public float getFoo() {
+            return foo;
+        }
+
+        public void setFoo(float foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagFloat extends SimpleTagSupport {
+
+        private Float foo;
+
+        public Float getFoo() {
+            return foo;
+        }
+
+        public void setFoo(Float foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagString extends SimpleTagSupport {
+
+        private String foo;
+
+        public String getFoo() {
+            return foo;
+        }
+
+        public void setFoo(String foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagTimeUnit extends SimpleTagSupport {
+
+        private TimeUnit foo;
+
+        public TimeUnit getFoo() {
+            return foo;
+        }
+
+        public void setFoo(TimeUnit foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagBigDecimal extends SimpleTagSupport {
+
+        private BigDecimal foo;
+
+        public BigDecimal getFoo() {
+            return foo;
+        }
+
+        public void setFoo(BigDecimal foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    public static class TagBigInteger extends SimpleTagSupport {
+
+        private BigInteger foo;
+
+        public BigInteger getFoo() {
+            return foo;
+        }
+
+        public void setFoo(BigInteger foo) {
+            this.foo = foo;
+        }
+
+        @Override
+        public void doTag() throws JspException, IOException {
+            getJspContext().getOut().print(foo);
+        }
+    }
+
+
+    /*
+     * Wrapper so we can use sensible names in the test labels
+     */
+    private static class ELInterpreterWrapper implements ELInterpreter {
+
+        private final boolean optimised;
+        private final String name;
+        private volatile ELInterpreter elInterpreter = null;
+
+        public ELInterpreterWrapper(boolean optimised, String name) {
+            this.optimised = optimised;
+            this.name = name;
+        }
+
+        @Override
+        public String interpreterCall(JspCompilationContext context, boolean isTagFile,
+                String expression, Class<?> expectedType, String fnmapvar) {
+            return getElInterpreter().interpreterCall(context, isTagFile, expression, expectedType, fnmapvar);
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+
+        // Lazy init to avoid LogManager init issues when running parameterized tests
+        private ELInterpreter getElInterpreter() {
+            if (elInterpreter == null) {
+                synchronized (this) {
+                    if (elInterpreter == null) {
+                        if (optimised) {
+                            elInterpreter = new ELInterpreterTagSetters();
+                        } else {
+                            elInterpreter = new ELInterpreterFactory.DefaultELInterpreter();
+                        }
+                    }
+                }
+            }
+            return elInterpreter;
+        }
+    }
+}
diff --git a/test/webapp/WEB-INF/tag-setters.tld b/test/webapp/WEB-INF/tag-setters.tld
new file mode 100644
index 0000000..e02ae14
--- /dev/null
+++ b/test/webapp/WEB-INF/tag-setters.tld
@@ -0,0 +1,245 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
+    version="2.0">
+    <tlib-version>1.0</tlib-version>
+    <short-name>tag-setters</short-name>
+    <uri>http://tomcat.apache.org/tag-setters</uri>
+    <tag>
+      <name>tagPrimitiveBoolean</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagPrimitiveBoolean</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>boolean</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagBoolean</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagBoolean</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>Boolean</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagPrimitiveCharacter</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagPrimitiveCharacter</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>char</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagCharacter</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagCharacter</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>Character</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagPrimitiveLong</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagPrimitiveLong</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>long</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagLong</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagLong</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>Long</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagPrimitiveInteger</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagPrimitiveInteger</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>int</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagInteger</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagInteger</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>Integer</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagPrimitiveShort</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagPrimitiveShort</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>short</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagShort</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagShort</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>Short</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagPrimitiveByte</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagPrimitiveByte</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>byte</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagByte</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagByte</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>Byte</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagPrimitiveDouble</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagPrimitiveDouble</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>double</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagDouble</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagDouble</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>Double</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagPrimitiveFloat</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagPrimitiveFloat</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>float</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagFloat</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagFloat</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>Float</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagBigDecimal</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagBigDecimal</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>BigDecimal</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagBigInteger</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagBigInteger</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>BigInteger</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagString</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagString</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>String</type>
+      </attribute>
+    </tag>
+    <tag>
+      <name>tagTimeUnit</name>
+      <tag-class>org.apache.jasper.optimizations.TestELInterpreterTagSetters$TagTimeUnit</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+        <name>foo</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+        <type>java.util.concurrent.TimeUnit</type>
+      </attribute>
+    </tag>
+</taglib>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-bigdecimal.jsp b/test/webapp/bug6nnnn/bug64872-bigdecimal.jsp
new file mode 100644
index 0000000..9bd18ed
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-bigdecimal.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 BigDecimal test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagBigDecimal foo="${'12.34'}" />]</p>
+    <p>02 The value of foo is [<ts:tagBigDecimal foo='${"12.34"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-biginteger.jsp b/test/webapp/bug6nnnn/bug64872-biginteger.jsp
new file mode 100644
index 0000000..0fcd1c6
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-biginteger.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 BigInteger test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagBigInteger foo="${'1234'}" />]</p>
+    <p>02 The value of foo is [<ts:tagBigInteger foo='${"1234"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-boolean.jsp b/test/webapp/bug6nnnn/bug64872-boolean.jsp
new file mode 100644
index 0000000..7bdc126
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-boolean.jsp
@@ -0,0 +1,37 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 Boolean.TRUE test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagBoolean foo="${false}" />]</p>
+    <p>02 The value of foo is [<ts:tagBoolean foo="${'false'}" />]</p>
+    <p>03 The value of foo is [<ts:tagBoolean foo='${"false"}' />]</p>
+    <p>04 The value of foo is [<ts:tagBoolean foo="${true}" />]</p>
+    <p>05 The value of foo is [<ts:tagBoolean foo="${'true'}" />]</p>
+    <p>06 The value of foo is [<ts:tagBoolean foo='${"true"}' />]</p>
+    <%--
+    <p>04 The value of foo is [<ts:tagBoolean foo="true" />]</p>
+    --%>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-byte.jsp b/test/webapp/bug6nnnn/bug64872-byte.jsp
new file mode 100644
index 0000000..8696a0a
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-byte.jsp
@@ -0,0 +1,32 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 Byte test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagByte foo="${'12'}" />]</p>
+    <p>02 The value of foo is [<ts:tagByte foo='${"12"}' />]</p>
+    <p>03 The value of foo is [<ts:tagByte foo="${'-12'}" />]</p>
+    <p>04 The value of foo is [<ts:tagByte foo='${"-12"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-character.jsp b/test/webapp/bug6nnnn/bug64872-character.jsp
new file mode 100644
index 0000000..0f6ec23
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-character.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 Character test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagCharacter foo="${'bar'}" />]</p>
+    <p>02 The value of foo is [<ts:tagCharacter foo='${"bar"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-double.jsp b/test/webapp/bug6nnnn/bug64872-double.jsp
new file mode 100644
index 0000000..799a0c0
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-double.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 Double test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagDouble foo="${'12.34'}" />]</p>
+    <p>02 The value of foo is [<ts:tagDouble foo='${"12.34"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-float.jsp b/test/webapp/bug6nnnn/bug64872-float.jsp
new file mode 100644
index 0000000..19d6226
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-float.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 Float test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagFloat foo="${'12.34'}" />]</p>
+    <p>02 The value of foo is [<ts:tagFloat foo='${"12.34"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-integer.jsp b/test/webapp/bug6nnnn/bug64872-integer.jsp
new file mode 100644
index 0000000..255d716
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-integer.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 Integer test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagInteger foo="${'1234'}" />]</p>
+    <p>02 The value of foo is [<ts:tagInteger foo='${"1234"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-long.jsp b/test/webapp/bug6nnnn/bug64872-long.jsp
new file mode 100644
index 0000000..2b7b4c8
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-long.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 Long test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagLong foo="${'1234'}" />]</p>
+    <p>02 The value of foo is [<ts:tagLong foo='${"1234"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-primitive-boolean.jsp b/test/webapp/bug6nnnn/bug64872-primitive-boolean.jsp
new file mode 100644
index 0000000..1f0073a
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-primitive-boolean.jsp
@@ -0,0 +1,37 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 boolean false test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagPrimitiveBoolean foo="${false}" />]</p>
+    <p>02 The value of foo is [<ts:tagPrimitiveBoolean foo="${'false'}" />]</p>
+    <p>03 The value of foo is [<ts:tagPrimitiveBoolean foo='${"false"}' />]</p>
+    <p>04 The value of foo is [<ts:tagPrimitiveBoolean foo="${true}" />]</p>
+    <p>05 The value of foo is [<ts:tagPrimitiveBoolean foo="${'true'}" />]</p>
+    <p>06 The value of foo is [<ts:tagPrimitiveBoolean foo='${"true"}' />]</p>
+    <%--
+    <p>04 The value of foo is [<ts:tagBoolean foo="false" />]</p>
+    --%>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-primitive-byte.jsp b/test/webapp/bug6nnnn/bug64872-primitive-byte.jsp
new file mode 100644
index 0000000..aed0705
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-primitive-byte.jsp
@@ -0,0 +1,32 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 byte test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagPrimitiveByte foo="${'12'}" />]</p>
+    <p>02 The value of foo is [<ts:tagPrimitiveByte foo='${"12"}' />]</p>
+    <p>03 The value of foo is [<ts:tagPrimitiveByte foo="${'-12'}" />]</p>
+    <p>04 The value of foo is [<ts:tagPrimitiveByte foo='${"-12"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-primitive-character.jsp b/test/webapp/bug6nnnn/bug64872-primitive-character.jsp
new file mode 100644
index 0000000..03762a6
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-primitive-character.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 char test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagPrimitiveCharacter foo="${'bar'}" />]</p>
+    <p>02 The value of foo is [<ts:tagPrimitiveCharacter foo='${"bar"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-primitive-double.jsp b/test/webapp/bug6nnnn/bug64872-primitive-double.jsp
new file mode 100644
index 0000000..b1cd64b
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-primitive-double.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 double test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagPrimitiveDouble foo="${'12.34'}" />]</p>
+    <p>02 The value of foo is [<ts:tagPrimitiveDouble foo='${"12.34"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-primitive-float.jsp b/test/webapp/bug6nnnn/bug64872-primitive-float.jsp
new file mode 100644
index 0000000..c574a845
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-primitive-float.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 float test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagPrimitiveFloat foo="${'12.34'}" />]</p>
+    <p>02 The value of foo is [<ts:tagPrimitiveFloat foo='${"12.34"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-primitive-integer.jsp b/test/webapp/bug6nnnn/bug64872-primitive-integer.jsp
new file mode 100644
index 0000000..059f217
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-primitive-integer.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 int test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagPrimitiveInteger foo="${'1234'}" />]</p>
+    <p>02 The value of foo is [<ts:tagPrimitiveInteger foo='${"1234"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-primitive-long.jsp b/test/webapp/bug6nnnn/bug64872-primitive-long.jsp
new file mode 100644
index 0000000..bf900f2
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-primitive-long.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 long test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagPrimitiveLong foo="${'1234'}" />]</p>
+    <p>02 The value of foo is [<ts:tagPrimitiveLong foo='${"1234"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-primitive-short.jsp b/test/webapp/bug6nnnn/bug64872-primitive-short.jsp
new file mode 100644
index 0000000..275ca58
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-primitive-short.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 short test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagPrimitiveShort foo="${'1234'}" />]</p>
+    <p>02 The value of foo is [<ts:tagPrimitiveShort foo='${"1234"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-short.jsp b/test/webapp/bug6nnnn/bug64872-short.jsp
new file mode 100644
index 0000000..cd4e19e
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-short.jsp
@@ -0,0 +1,30 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 Short test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagShort foo="${'1234'}" />]</p>
+    <p>02 The value of foo is [<ts:tagShort foo='${"1234"}' />]</p>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-string.jsp b/test/webapp/bug6nnnn/bug64872-string.jsp
new file mode 100644
index 0000000..4dfa711
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-string.jsp
@@ -0,0 +1,33 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 String test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagString foo="${'bar'}" />]</p>
+    <p>02 The value of foo is [<ts:tagString foo='${"bar"}' />]</p>
+    <%--
+    <p>03 The value of foo is [<ts:tagString foo="bar" />]</p>
+    --%>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/test/webapp/bug6nnnn/bug64872-timeunit.jsp b/test/webapp/bug6nnnn/bug64872-timeunit.jsp
new file mode 100644
index 0000000..e21bc2d
--- /dev/null
+++ b/test/webapp/bug6nnnn/bug64872-timeunit.jsp
@@ -0,0 +1,33 @@
+<%--
+ 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.
+--%>
+<%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
+<html>
+  <head><title>Bug 64872 TimeUnit test case</title></head>
+  <body>
+  <%
+  for (int i=0; i < 1000000; i++) {
+  %>
+    <p>01 The value of foo is [<ts:tagTimeUnit foo="${'SECONDS'}" />]</p>
+    <p>02 The value of foo is [<ts:tagTimeUnit foo='${"SECONDS"}' />]</p>
+    <%--
+    <p>03 The value of foo is [<ts:tagTimeUnit foo="SECONDS" />]</p>
+     --%>
+  <%
+  }
+  %>
+  </body>
+</html>
\ No newline at end of file
diff --git a/webapps/docs/jasper-howto.xml b/webapps/docs/jasper-howto.xml
index 56e731f..aea993c 100644
--- a/webapps/docs/jasper-howto.xml
+++ b/webapps/docs/jasper-howto.xml
@@ -399,8 +399,13 @@ with Jasper.
 The second extension point is the Expression Language interpreter. Alternative
 interpreters may be configured through the <code>ServletContext</code>. See the
 <code>ELInterpreterFactory</code> javadoc for details of how to configure an
-alternative EL interpreter.
+alternative EL interpreter. A alternative interpreter primarily targetting tag
+settings is provided at
+<code>org.apache.jasper.optimizations.ELInterpreterTagSetters</code>. See the
+javadoc for details of the optimisations and the impact they have on
+specification compliance.
 </p>
+
 </section>
 
 </body>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


[tomcat] 02/02: BZ 64872 Optimised StringInterpreter for Enum Values

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 616949edde000602ba52368bc935207f85f07af7
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Wed Nov 25 12:15:23 2020 +0000

    BZ 64872 Optimised StringInterpreter for Enum Values
    
    This is not strictly spec compliant hence it isn't enabled by
    default but it should work for many applicaitons.
    https://bz.apache.org/bugzilla/show_bug.cgi?id=64872
---
 .../optimizations/StringInterpreterEnum.java       |  37 +++++++
 .../optimizations/TestELInterpreterTagSetters.java |   3 +
 .../TestStringInterpreterTagSetters.java           | 120 +++++++++++++++++++++
 test/webapp/bug6nnnn/bug64872-timeunit.jsp         |   3 -
 ...ug64872-timeunit.jsp => bug64872b-timeunit.jsp} |  10 +-
 webapps/docs/jasper-howto.xml                      |   7 ++
 6 files changed, 170 insertions(+), 10 deletions(-)

diff --git a/java/org/apache/jasper/optimizations/StringInterpreterEnum.java b/java/org/apache/jasper/optimizations/StringInterpreterEnum.java
new file mode 100644
index 0000000..b85f339
--- /dev/null
+++ b/java/org/apache/jasper/optimizations/StringInterpreterEnum.java
@@ -0,0 +1,37 @@
+/*
+ * 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.jasper.optimizations;
+
+import org.apache.jasper.compiler.StringInterpreterFactory.DefaultStringInterpreter;
+
+/**
+ * Provides an optimised conversion of string values to Enums. It bypasses the
+ * check for registered PropertyEditor.
+ */
+public class StringInterpreterEnum extends DefaultStringInterpreter {
+
+    @Override
+    protected String coerceToOtherType(Class<?> c, String s, boolean isNamedAttribute) {
+        if (c.isEnum() && !isNamedAttribute) {
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            Enum<?> enumValue = Enum.valueOf((Class<? extends Enum>) c, s);
+            return c.getName() + "." + enumValue.name();
+        }
+
+        return null;
+    }
+}
diff --git a/test/org/apache/jasper/optimizations/TestELInterpreterTagSetters.java b/test/org/apache/jasper/optimizations/TestELInterpreterTagSetters.java
index 5a6cf8f..ac66ddd 100644
--- a/test/org/apache/jasper/optimizations/TestELInterpreterTagSetters.java
+++ b/test/org/apache/jasper/optimizations/TestELInterpreterTagSetters.java
@@ -40,6 +40,7 @@ import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.jasper.JspCompilationContext;
 import org.apache.jasper.compiler.ELInterpreter;
 import org.apache.jasper.compiler.ELInterpreterFactory;
+import org.apache.jasper.compiler.StringInterpreter;
 import org.apache.tomcat.util.buf.ByteChunk;
 
 @RunWith(Parameterized.class)
@@ -112,6 +113,8 @@ public class TestELInterpreterTagSetters extends TomcatBaseTest {
         Context ctxt = (Context) tomcat.getHost().findChild("/test");
         ctxt.getServletContext().setAttribute(ELInterpreter.class.getCanonicalName(), elInterpreter);
 
+        ctxt.getServletContext().setAttribute(StringInterpreter.class.getCanonicalName(), new StringInterpreterEnum());
+
         ByteChunk bc = getUrl("http://localhost:" + getPort() + "/test/bug6nnnn/bug64872-" + target + ".jsp");
 
         String actual = bc.toString();
diff --git a/test/org/apache/jasper/optimizations/TestStringInterpreterTagSetters.java b/test/org/apache/jasper/optimizations/TestStringInterpreterTagSetters.java
new file mode 100644
index 0000000..3a75b75
--- /dev/null
+++ b/test/org/apache/jasper/optimizations/TestStringInterpreterTagSetters.java
@@ -0,0 +1,120 @@
+/*
+ * 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.jasper.optimizations;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.jasper.compiler.StringInterpreter;
+import org.apache.jasper.compiler.StringInterpreterFactory;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+@RunWith(Parameterized.class)
+public class TestStringInterpreterTagSetters extends TomcatBaseTest {
+
+    @Parameters(name="{index}: {0}")
+    public static Collection<Object[]> parameters() {
+        List<Object[]> parameterSets = new ArrayList<>();
+
+        StringInterpreter[] stringInterpreters = new StringInterpreter[] {
+                // Warm-up
+                // First call will trigger compilation (and therefore be slower)
+                // Call both to ensure both are warmed up
+                new StringInterpreterWrapper(true, "Enum"),
+                new StringInterpreterWrapper(false, "Default"),
+                // Compare times of these test runs
+                new StringInterpreterWrapper(true, "Enum"),
+                new StringInterpreterWrapper(false, "Default"),
+                };
+
+        for (StringInterpreter stringInterpreter : stringInterpreters) {
+            parameterSets.add(new Object[] { stringInterpreter });
+        }
+        return parameterSets;
+    }
+
+
+    @Parameter(0)
+    public StringInterpreter stringInterpreter;
+
+    @Test
+    public void testTag() throws Exception {
+        Tomcat tomcat = getTomcatInstanceTestWebapp(false, true);
+        Context ctxt = (Context) tomcat.getHost().findChild("/test");
+        ctxt.getServletContext().setAttribute(StringInterpreter.class.getCanonicalName(), stringInterpreter);
+
+        ByteChunk bc = getUrl("http://localhost:" + getPort() + "/test/bug6nnnn/bug64872b-timeunit.jsp");
+
+        String actual = bc.toString();
+
+        Assert.assertTrue(actual, actual.contains("01 The value of foo is [SECONDS]"));
+    }
+
+
+    /*
+     * Wrapper so we can use sensible names in the test labels
+     */
+    private static class StringInterpreterWrapper implements StringInterpreter {
+
+        private final boolean optimised;
+        private final String name;
+        private volatile StringInterpreter stringInterpreter = null;
+
+        public StringInterpreterWrapper(boolean optimised, String name) {
+            this.optimised = optimised;
+            this.name = name;
+        }
+
+        @Override
+        public String convertString(Class<?> c, String s, String attrName, Class<?> propEditorClass,
+                boolean isNamedAttribute) {
+            return getStringInterpreter().convertString(c, s, attrName, propEditorClass, isNamedAttribute) ;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+
+        // Lazy init to avoid LogManager init issues when running parameterized tests
+        private StringInterpreter getStringInterpreter() {
+            if (stringInterpreter == null) {
+                synchronized (this) {
+                    if (stringInterpreter == null) {
+                        if (optimised) {
+                            stringInterpreter = new StringInterpreterEnum();
+                        } else {
+                            stringInterpreter = new StringInterpreterFactory.DefaultStringInterpreter();
+                        }
+                    }
+                }
+            }
+            return stringInterpreter;
+        }
+    }
+}
diff --git a/test/webapp/bug6nnnn/bug64872-timeunit.jsp b/test/webapp/bug6nnnn/bug64872-timeunit.jsp
index e21bc2d..00b71ba 100644
--- a/test/webapp/bug6nnnn/bug64872-timeunit.jsp
+++ b/test/webapp/bug6nnnn/bug64872-timeunit.jsp
@@ -23,9 +23,6 @@
   %>
     <p>01 The value of foo is [<ts:tagTimeUnit foo="${'SECONDS'}" />]</p>
     <p>02 The value of foo is [<ts:tagTimeUnit foo='${"SECONDS"}' />]</p>
-    <%--
-    <p>03 The value of foo is [<ts:tagTimeUnit foo="SECONDS" />]</p>
-     --%>
   <%
   }
   %>
diff --git a/test/webapp/bug6nnnn/bug64872-timeunit.jsp b/test/webapp/bug6nnnn/bug64872b-timeunit.jsp
similarity index 73%
copy from test/webapp/bug6nnnn/bug64872-timeunit.jsp
copy to test/webapp/bug6nnnn/bug64872b-timeunit.jsp
index e21bc2d..679925f 100644
--- a/test/webapp/bug6nnnn/bug64872-timeunit.jsp
+++ b/test/webapp/bug6nnnn/bug64872b-timeunit.jsp
@@ -16,16 +16,12 @@
 --%>
 <%@ taglib uri="http://tomcat.apache.org/tag-setters" prefix="ts" %>
 <html>
-  <head><title>Bug 64872 TimeUnit test case</title></head>
+  <head><title>Bug 64872b TimeUnit test case</title></head>
   <body>
   <%
-  for (int i=0; i < 1000000; i++) {
+  for (int i=0; i < 100000; i++) {
   %>
-    <p>01 The value of foo is [<ts:tagTimeUnit foo="${'SECONDS'}" />]</p>
-    <p>02 The value of foo is [<ts:tagTimeUnit foo='${"SECONDS"}' />]</p>
-    <%--
-    <p>03 The value of foo is [<ts:tagTimeUnit foo="SECONDS" />]</p>
-     --%>
+    <p>01 The value of foo is [<ts:tagTimeUnit foo="SECONDS" />]</p>
   <%
   }
   %>
diff --git a/webapps/docs/jasper-howto.xml b/webapps/docs/jasper-howto.xml
index aea993c..850d0d3 100644
--- a/webapps/docs/jasper-howto.xml
+++ b/webapps/docs/jasper-howto.xml
@@ -406,6 +406,13 @@ javadoc for details of the optimisations and the impact they have on
 specification compliance.
 </p>
 
+<p>
+An extension point is also provided for coercion of String values to Enums. It
+is provided at
+<code>org.apache.jasper.optimizations.StringInterpreterEnum</code>. See the
+javadoc for details of the optimisations and the impact they have on
+specification compliance.
+</p>
 </section>
 
 </body>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org