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:30 UTC

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

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