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

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


The following commit(s) were added to refs/heads/10.1.x by this push:
     new 9192fc8a84 Adding nonstandard support for c:set and c:remove
9192fc8a84 is described below

commit 9192fc8a849f425444f8dacd3d12595fd566f5d7
Author: John Engebretson <jeng...@amazon.com>
AuthorDate: Tue Apr 22 19:51:01 2025 +0000

    Adding nonstandard support for c:set and c:remove
---
 java/org/apache/jasper/EmbeddedServletOptions.java |  12 ++
 java/org/apache/jasper/JspC.java                   |  16 ++
 java/org/apache/jasper/Options.java                |   8 +
 java/org/apache/jasper/compiler/Generator.java     | 227 +++++++++++++++++++++
 .../apache/jasper/runtime/JspRuntimeLibrary.java   |  27 +++
 test/jakarta/servlet/jsp/TesterPageContext.java    |  12 +-
 .../jsp/TesterPageContextWithAttributes.java       | 126 ++++++++++++
 test/org/apache/jasper/compiler/TestGenerator.java |  86 ++++++++
 .../compiler/TestNonstandardTagPerformance.java    | 148 ++++++++++++++
 .../jasper/runtime/TestJspRuntimeLibrary.java      |  37 ++++
 test/webapp/WEB-INF/web.xml                        |  28 +++
 .../webapp/jsp/generator/nonstandard/remove-01.jsp |  26 +++
 .../webapp/jsp/generator/nonstandard/remove-02.jsp |  26 +++
 .../webapp/jsp/generator/nonstandard/remove-03.jsp |  26 +++
 .../webapp/jsp/generator/nonstandard/remove-04.jsp |  26 +++
 .../webapp/jsp/generator/nonstandard/remove-05.jsp |  26 +++
 test/webapp/jsp/generator/nonstandard/set-01.jsp   |  22 ++
 test/webapp/jsp/generator/nonstandard/set-02.jsp   |  22 ++
 test/webapp/jsp/generator/nonstandard/set-03.jsp   |  22 ++
 test/webapp/jsp/generator/nonstandard/set-04.jsp   |  22 ++
 test/webapp/jsp/generator/nonstandard/set-05.jsp   |  22 ++
 webapps/docs/changelog.xml                         |   5 +
 22 files changed, 970 insertions(+), 2 deletions(-)

diff --git a/java/org/apache/jasper/EmbeddedServletOptions.java 
b/java/org/apache/jasper/EmbeddedServletOptions.java
index 49ec95b63e..e9d212e89d 100644
--- a/java/org/apache/jasper/EmbeddedServletOptions.java
+++ b/java/org/apache/jasper/EmbeddedServletOptions.java
@@ -223,6 +223,8 @@ public final class EmbeddedServletOptions implements 
Options {
 
     private boolean useInstanceManagerForTags = false;
 
+    private String useNonstandardTagOptimizations;
+
     public String getProperty(String name) {
         return settings.getProperty(name);
     }
@@ -470,6 +472,11 @@ public final class EmbeddedServletOptions implements 
Options {
         return useInstanceManagerForTags;
     }
 
+    @Override
+    public String getUseNonstandardTagOptimizations() {
+        return useNonstandardTagOptimizations;
+    }
+
     /**
      * Create an EmbeddedServletOptions object using data available from 
ServletConfig and ServletContext.
      *
@@ -652,6 +659,11 @@ public final class EmbeddedServletOptions implements 
Options {
             this.classpath = classpath;
         }
 
+        String useNonstandardTagOptimizations = 
config.getInitParameter("useNonstandardTagOptimizations");
+        if (useNonstandardTagOptimizations != null) {
+            this.useNonstandardTagOptimizations = 
useNonstandardTagOptimizations;
+        }
+
         /*
          * scratchdir
          */
diff --git a/java/org/apache/jasper/JspC.java b/java/org/apache/jasper/JspC.java
index 8417538557..2bbde73f59 100644
--- a/java/org/apache/jasper/JspC.java
+++ b/java/org/apache/jasper/JspC.java
@@ -268,6 +268,8 @@ public class JspC extends Task implements Options {
     protected boolean fullstop = false;
     protected String[] args;
 
+    protected String useNonstandardTagOptimizations;
+
     public static void main(String[] arg) {
         if (arg.length == 0) {
             System.out.println(Localizer.getMessage("jspc.usage"));
@@ -995,6 +997,15 @@ public class JspC extends Task implements Options {
         failOnError = b;
     }
 
+    /**
+     * Sets the set of custom tags to use nonstandard optimizations.
+     *
+     * @param useNonstandardTagOptimizations which tags to override
+     */
+    public void setUseNonstandardTagOptimizations(String 
useNonstandardTagOptimizations) {
+        this.useNonstandardTagOptimizations = useNonstandardTagOptimizations;
+    }
+
     /**
      * @return <code>true</code> if an exception will be thrown in case of a 
compilation error.
      */
@@ -1722,6 +1733,11 @@ public class JspC extends Task implements Options {
         }
     }
 
+    @Override
+    public String getUseNonstandardTagOptimizations() {
+        return useNonstandardTagOptimizations;
+    }
+
 
     private class ProcessFile implements Callable<Void> {
         private final String file;
diff --git a/java/org/apache/jasper/Options.java 
b/java/org/apache/jasper/Options.java
index 721a66ce88..b0802f8e1c 100644
--- a/java/org/apache/jasper/Options.java
+++ b/java/org/apache/jasper/Options.java
@@ -342,4 +342,12 @@ public interface Options {
     default boolean getGeneratedJavaAddTimestamp() {
         return true;
     }
+
+    /**
+     * A string containing a comma-separated list of names to which custom tag 
implementations should be applied.
+     * Unknown or unused tag entries are harmless.  Generally defined via an 
init parameter on the JspServlet.
+     *
+     * @return which tags to use
+     */
+    String getUseNonstandardTagOptimizations();
 }
diff --git a/java/org/apache/jasper/compiler/Generator.java 
b/java/org/apache/jasper/compiler/Generator.java
index b7aab2f3af..f14b8554d7 100644
--- a/java/org/apache/jasper/compiler/Generator.java
+++ b/java/org/apache/jasper/compiler/Generator.java
@@ -51,6 +51,7 @@ import org.apache.jasper.JasperException;
 import org.apache.jasper.JspCompilationContext;
 import org.apache.jasper.TrimSpacesOption;
 import org.apache.jasper.compiler.Node.ChildInfoBase;
+import org.apache.jasper.compiler.Node.JspAttribute;
 import org.apache.jasper.compiler.Node.NamedAttribute;
 import org.apache.jasper.runtime.JspRuntimeLibrary;
 import org.apache.juli.logging.Log;
@@ -74,6 +75,8 @@ class Generator {
 
     private static final Pattern BLANK_LINE_PATTERN = 
Pattern.compile("(\\s*(\\n|\\r)+\\s*)");
 
+    private static final String CORE_LIBS_URI = 
"http://java.sun.com/jsp/jstl/core";;
+
     private final ServletWriter out;
 
     private final ArrayList<GenBuffer> methodsBuffered;
@@ -106,6 +109,8 @@ class Generator {
 
     private final ELInterpreter elInterpreter;
 
+    private final Set<String> nonstandardCustomTagNames;
+
     private final StringInterpreter stringInterpreter;
 
     /**
@@ -1499,6 +1504,9 @@ class Generator {
 
         @Override
         public void visit(Node.CustomTag n) throws JasperException {
+            if (visitPotentiallyNonstandardCustomTag(n)) {
+                return;
+            }
 
             // Use plugin to generate more efficient code if there is one.
             if (n.useTagPlugin()) {
@@ -3048,6 +3056,219 @@ class Generator {
 
             return varName;
         }
+
+        /**
+         * Determines whether a tag should be handled via nonstandard code 
(typically
+         * faster). Considers both configuration and level of support within 
Tomcat.
+         *
+         * Note that Tomcat is free to ignore any case it cannot handle, as 
long as it
+         * reports it accurately to the caller by returning false. For 
example, the
+         * initial implementation for c:set excludes support for body content. 
c:set
+         * tags with body content will be generated with the standard code and 
tags
+         * without body content will be generated via non-standard code.
+         *
+         * @param n tag
+         * @param jspAttributes jsp attributes
+         * @return whether code was generated
+         * @throws JasperException unexpected error
+         */
+        private boolean visitPotentiallyNonstandardCustomTag(Node.CustomTag n)
+                throws JasperException {
+            if (!nonstandardCustomTagNames.contains(n.getQName())) {
+                // tag is not configured, move along
+                return false;
+            }
+
+            // collect the attributes into one Map for further checks
+            Map<String, JspAttribute> jspAttributes = new HashMap<>();
+            if (n.getJspAttributes() != null) {
+                for (JspAttribute jspAttr : n.getJspAttributes()) {
+                    jspAttributes.put(jspAttr.getLocalName(), jspAttr);
+                }
+            }
+            switch (n.qName) {
+            case "c:set":
+                // requires var and value, scope is optional, body is 
prohibited, value cannot be deferred
+                if (n.hasEmptyBody()
+                        && jspAttributes.containsKey("var")
+                        && jspAttributes.containsKey("value")
+                        && CORE_LIBS_URI.equals(n.getURI())) {
+                    // verify value is not a deferred expression
+                    String valueText = jspAttributes.get("value").getValue();
+                    if (valueText.startsWith("#")) {
+                        return false;
+                    } else if (jspAttributes.size() == 2
+                            || (jspAttributes.size() == 3 && 
jspAttributes.containsKey("scope"))) {
+                        generateNonstandardSetLogic(n, jspAttributes);
+                        return true;
+                    }
+                }
+                break;
+            case "c:remove":
+                // requires var, scope is optional, body is prohibited
+                if (n.hasEmptyBody()
+                        && jspAttributes.containsKey("var")
+                        && CORE_LIBS_URI.equals(n.getURI())
+                        && (jspAttributes.size() == 1
+                                || (jspAttributes.size() == 2
+                                && jspAttributes.containsKey("scope")))) {
+                    generateNonstandardRemoveLogic(n, jspAttributes);
+                    return true;
+
+                }
+                break;
+            default:
+                // This indicates someone configured a tag with no 
non-standard implementation.
+                // Harmless, fall back to the standard implementation.
+            }
+            return false;
+        }
+
+        private void generateNonstandardSetLogic(Node.CustomTag n, Map<String, 
JspAttribute> jspAttributes)
+                throws JasperException {
+            String baseVar = createTagVarName(n.getQName(), n.getPrefix(), 
n.getLocalName());
+            String tagMethod = "_jspx_meth_" + baseVar;
+            ServletWriter outSave = out;
+
+            // generate method call
+            out.printin(tagMethod);
+            out.print("(");
+            out.print("_jspx_page_context");
+            out.println(");");
+            GenBuffer genBuffer = new GenBuffer(n, n.getBody());
+            methodsBuffered.add(genBuffer);
+            out = genBuffer.getOut();
+
+            // Generate code for method declaration
+            methodNesting++;
+            out.println();
+            out.pushIndent();
+            out.printin("private void ");
+            out.print(tagMethod);
+            out.println("(jakarta.servlet.jsp.PageContext 
_jspx_page_context)");
+            out.printil("        throws java.lang.Throwable {");
+            out.pushIndent();
+            // Generated body of method
+            out.printil("//  " + n.getQName());
+
+            JspAttribute varAttribute = jspAttributes.get("var");
+            Mark m = n.getStart();
+            out.printil("// " + m.getFile() + "(" + m.getLineNumber() + "," + 
m.getColumnNumber() + ") "
+                    + varAttribute.getTagAttributeInfo());
+
+            JspAttribute valueAttribute = jspAttributes.get("value");
+            m = n.getStart();
+            out.printil("// " + m.getFile() + "(" + m.getLineNumber() + "," + 
m.getColumnNumber() + ") "
+                    + valueAttribute.getTagAttributeInfo());
+
+            String varValue = varAttribute.getValue();
+
+            // get the scope constant at compile-time, where blank means page
+            String scopeValue = translateScopeToConstant(jspAttributes);
+
+            // translates the specified value attributes into 
EL-interpretation code using standard logic
+            String evaluatedAttribute = 
evaluateAttribute(getTagHandlerInfo(n), valueAttribute,
+                    n, null);
+
+            // call the multi-line logic equivalent of SetTag
+            
out.printil("org.apache.jasper.runtime.JspRuntimeLibrary.nonstandardSetTag(_jspx_page_context,
 \""
+                    + varValue + "\", " + evaluatedAttribute + ", " + 
scopeValue + ");");
+
+            // Generate end of method
+            out.popIndent();
+            out.printil("}");
+            out.popIndent();
+
+            methodNesting--;
+            // restore previous writer
+            out = outSave;
+        }
+
+        /**
+         * Compile-time translation of the scope variable into the constant 
equivalent. Avoids runtime evaluation as
+         * performed by SetTag.  Unspecified scope means page.
+         *
+         * @param jspAttributes attributes
+         * @return equivalent constant from PageContext
+         */
+        private String translateScopeToConstant(Map<String, JspAttribute> 
jspAttributes) {
+            String scopeValue;
+            JspAttribute scopeAttribute = jspAttributes.get("scope");
+            if (scopeAttribute == null) {
+                scopeValue = "jakarta.servlet.jsp.PageContext.PAGE_SCOPE";
+            } else {
+                switch (scopeAttribute.getValue()) {
+                case "":
+                case "page":
+                    scopeValue = "jakarta.servlet.jsp.PageContext.PAGE_SCOPE";
+                    break;
+                case "request":
+                    scopeValue = 
"jakarta.servlet.jsp.PageContext.REQUEST_SCOPE";
+                    break;
+                case "session":
+                    scopeValue = 
"jakarta.servlet.jsp.PageContext.SESSION_SCOPE";
+                    break;
+                case "application":
+                    scopeValue = 
"jakarta.servlet.jsp.PageContext.APPLICATION_SCOPE";
+                    break;
+                default:
+                    throw new 
IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope"));
+                }
+            }
+            return scopeValue;
+        }
+
+        /**
+         * Generates the code for a non-standard remove.  Note that removes 
w/o a specified scope will remove from all scopes.
+         *
+         * @param n tag
+         * @param jspAttributes attributes
+         * @throws JasperException unspecified error
+         */
+        private void generateNonstandardRemoveLogic(Node.CustomTag n, 
Map<String, JspAttribute> jspAttributes)
+                throws JasperException {
+            String baseVar = createTagVarName(n.getQName(), n.getPrefix(), 
n.getLocalName());
+            String tagMethod = "_jspx_meth_" + baseVar;
+            ServletWriter outSave = out;
+
+            // generate method call
+            out.printin(tagMethod);
+            out.print("(");
+            out.print("_jspx_page_context");
+            out.println(");");
+            GenBuffer genBuffer = new GenBuffer(n, n.getBody());
+            methodsBuffered.add(genBuffer);
+            out = genBuffer.getOut();
+
+            // Generate code for method declaration
+            methodNesting++;
+            out.println();
+            out.pushIndent();
+            out.printin("private void ");
+            out.print(tagMethod);
+            out.println("(jakarta.servlet.jsp.PageContext pageContext)");
+            out.printil("        throws java.lang.Throwable {");
+            out.pushIndent();
+            // Generated body of method
+            String varValue = jspAttributes.get("var").getValue();
+            JspAttribute scope = jspAttributes.get("scope");
+            if (scope == null) {
+                // c:remove without a scope means remove from all scopes
+                out.printil("pageContext.removeAttribute(\"" + varValue + 
"\");");
+            } else {
+                // c:remove with a scope means remove only from the specified 
scope
+                String scopeValue = translateScopeToConstant(jspAttributes);
+                out.printil("pageContext.removeAttribute(\"" + varValue + "\", 
" + scopeValue + ");");
+            }
+            // Generate end of method
+            out.popIndent();
+            out.printil("}");
+            out.popIndent();
+
+            methodNesting--;
+            // restore previous writer
+            out = outSave;
+        }
     }
 
     private static void generateLocalVariables(ServletWriter out, 
ChildInfoBase n) {
@@ -3190,6 +3411,12 @@ class Generator {
         }
         timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         timestampFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+        String nonstandardOptionsList = 
ctxt.getOptions().getUseNonstandardTagOptimizations();
+        if (nonstandardOptionsList == null) {
+            nonstandardCustomTagNames = Collections.emptySet();
+        } else {
+            nonstandardCustomTagNames = new 
HashSet<>(Arrays.asList(nonstandardOptionsList.split(",")));
+        }
     }
 
     /**
diff --git a/java/org/apache/jasper/runtime/JspRuntimeLibrary.java 
b/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
index 7b584f8a85..fb9af4fd7a 100644
--- a/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
+++ b/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
@@ -25,6 +25,7 @@ import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.Enumeration;
 
+import jakarta.el.VariableMapper;
 import jakarta.servlet.RequestDispatcher;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.ServletRequest;
@@ -964,4 +965,30 @@ public class JspRuntimeLibrary {
         }
 
     }
+
+    /**
+     * This method parallels the logic of {@code SetSupport.doEndTag()}.
+     *
+     * @param pageContext pageContext
+     * @param var name of the variable
+     * @param value value to store
+     * @param scope scope
+     */
+    public static void nonstandardSetTag(jakarta.servlet.jsp.PageContext 
pageContext, String var, Object value,
+            int scope) {
+        if (value == null) {
+            // matches SetTag and removes the key from the specified scope
+            pageContext.removeAttribute(var, scope);
+        } else {
+            if (scope == PageContext.PAGE_SCOPE) {
+                // matches SetTag and cleans up the VariableMapper
+                VariableMapper vm = 
pageContext.getELContext().getVariableMapper();
+                if (vm != null) {
+                    vm.setVariable(var, null);
+                }
+            }
+            // does the all-important set of the correct scope
+            pageContext.setAttribute(var, value, scope);
+        }
+    }
 }
diff --git a/test/jakarta/servlet/jsp/TesterPageContext.java 
b/test/jakarta/servlet/jsp/TesterPageContext.java
index 59814ca097..ea1e44b8cb 100644
--- a/test/jakarta/servlet/jsp/TesterPageContext.java
+++ b/test/jakarta/servlet/jsp/TesterPageContext.java
@@ -29,6 +29,15 @@ import jakarta.servlet.ServletResponse;
 import jakarta.servlet.http.HttpSession;
 
 public class TesterPageContext extends PageContext {
+    private final ELContext elContext;
+
+    public TesterPageContext() {
+        this.elContext = null;
+    }
+
+    public TesterPageContext(ELContext elContext) {
+        this.elContext = elContext;
+    }
 
     @Override
     public void initialize(Servlet servlet, ServletRequest request,
@@ -181,8 +190,7 @@ public class TesterPageContext extends PageContext {
 
     @Override
     public ELContext getELContext() {
-        // NO-OP
-        return null;
+        return elContext;
     }
 
     @Override
diff --git a/test/jakarta/servlet/jsp/TesterPageContextWithAttributes.java 
b/test/jakarta/servlet/jsp/TesterPageContextWithAttributes.java
new file mode 100644
index 0000000000..bf93e09493
--- /dev/null
+++ b/test/jakarta/servlet/jsp/TesterPageContextWithAttributes.java
@@ -0,0 +1,126 @@
+/*
+ * 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 jakarta.servlet.jsp;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import jakarta.el.ELContext;
+
+import org.apache.jasper.compiler.Localizer;
+
+public class TesterPageContextWithAttributes extends TesterPageContext {
+    private final Map<String, Object> applicationAttributes = new HashMap<>();
+    private final Map<String, Object> pageAttributes = new HashMap<>();
+    private final Map<String, Object> requestAttributes = new HashMap<>();
+    private final Map<String, Object> sessionAttributes = new HashMap<>();
+
+    public TesterPageContextWithAttributes() {
+        super();
+    }
+
+    public TesterPageContextWithAttributes(ELContext elContext) {
+        super(elContext);
+    }
+
+    @Override
+    public Object getAttribute(String name) {
+        return getAttribute(name, PAGE_SCOPE);
+    }
+
+    @Override
+    public Object getAttribute(String name, int scope) {
+        if (name == null) {
+            throw new 
NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name"));
+        }
+
+        switch (scope) {
+        case PAGE_SCOPE:
+               return pageAttributes.get(name);
+        case REQUEST_SCOPE:
+               return requestAttributes.get(name);
+        case SESSION_SCOPE:
+               return sessionAttributes.get(name);
+        case APPLICATION_SCOPE:
+               return applicationAttributes.get(name);
+        default:
+               throw new 
IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope"));
+        }
+    }
+
+    @Override
+    public void removeAttribute(String name) {
+        removeAttribute(name, PAGE_SCOPE);
+        removeAttribute(name, REQUEST_SCOPE);
+        removeAttribute(name, SESSION_SCOPE);
+        removeAttribute(name, APPLICATION_SCOPE);
+    }
+
+    @Override
+    public void removeAttribute(String name, int scope) {
+        switch (scope) {
+        case PageContext.APPLICATION_SCOPE:
+            applicationAttributes.remove(name);
+            break;
+        case PageContext.PAGE_SCOPE:
+            pageAttributes.remove(name);
+            break;
+        case PageContext.REQUEST_SCOPE:
+            requestAttributes.remove(name);
+            break;
+        case PageContext.SESSION_SCOPE:
+            sessionAttributes.remove(name);
+            break;
+        default:
+            throw new 
IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope"));
+        }
+    }
+
+    @Override
+    public void setAttribute(String name, Object value) {
+        setAttribute(name, value, PAGE_SCOPE);
+    }
+
+    @Override
+    public void setAttribute(String name, Object value, int scope) {
+        if (value == null) {
+            removeAttribute(name, scope);
+        } else {
+            switch (scope) {
+            case PAGE_SCOPE:
+                pageAttributes.put(name, value);
+                break;
+
+            case REQUEST_SCOPE:
+                requestAttributes.put(name, value);
+                break;
+
+            case SESSION_SCOPE:
+                sessionAttributes.put(name, value);
+                break;
+
+            case APPLICATION_SCOPE:
+                applicationAttributes.put(name, value);
+                break;
+
+            default:
+                throw new 
IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope"));
+            }
+        }
+    }
+
+}
diff --git a/test/org/apache/jasper/compiler/TestGenerator.java 
b/test/org/apache/jasper/compiler/TestGenerator.java
index c6abab5a1b..459af3e666 100644
--- a/test/org/apache/jasper/compiler/TestGenerator.java
+++ b/test/org/apache/jasper/compiler/TestGenerator.java
@@ -394,6 +394,49 @@ public class TestGenerator extends TomcatBaseTest {
         doTestJspId(true);
     }
 
+    @Test
+    public void testNonstandardSets() throws Exception {
+        getTomcatInstanceTestWebapp(true, true);
+
+        // This should break all subsequent requests
+        ByteChunk body = new ByteChunk();
+        getUrl("http://localhost:"; + getPort() + 
"/test/jsp/generator/nonstandard/set-01.jsp", body, null);
+        Assert.assertEquals("\n\n\n"
+                + "pageContext value=testValue\n"
+                + "request value=null\n"
+                + "session value=null\n"
+                + "application value=null", body.toString());
+        body.recycle();
+        getUrl("http://localhost:"; + getPort() + 
"/test/jsp/generator/nonstandard/set-02.jsp", body, null);
+        Assert.assertEquals("\n\n\n"
+                + "pageContext value=testValue\n"
+                + "request value=null\n"
+                + "session value=null\n"
+                + "application value=null", body.toString());
+        body.recycle();
+        getUrl("http://localhost:"; + getPort() + 
"/test/jsp/generator/nonstandard/set-03.jsp", body, null);
+        Assert.assertEquals("\n\n\n"
+                + "pageContext value=null\n"
+                + "request value=testValue\n"
+                + "session value=null\n"
+                + "application value=null", body.toString());
+        body.recycle();
+        getUrl("http://localhost:"; + getPort() + 
"/test/jsp/generator/nonstandard/set-04.jsp", body, null);
+        Assert.assertEquals("\n\n\n"
+                + "pageContext value=null\n"
+                + "request value=null\n"
+                + "session value=testValue\n"
+                + "application value=null", body.toString());
+        body.recycle();
+        getUrl("http://localhost:"; + getPort() + 
"/test/jsp/generator/nonstandard/set-05.jsp", body, null);
+        Assert.assertEquals("\n\n\n"
+                + "pageContext value=null\n"
+                + "request value=null\n"
+                + "session value=null\n"
+                + "application value=testValue", body.toString());
+        body.recycle();
+    }
+
     private void doTestJspId(boolean document) throws Exception {
         getTomcatInstanceTestWebapp(false, true);
 
@@ -1061,4 +1104,47 @@ public class TestGenerator extends TomcatBaseTest {
 
         Assert.assertEquals(body.toString(), expectedResponseCode, rc);
     }
+
+    @Test
+    public void testNonstandardRemoves() throws Exception {
+        getTomcatInstanceTestWebapp(true, true);
+
+        // This should break all subsequent requests
+        ByteChunk body = new ByteChunk();
+        getUrl("http://localhost:"; + getPort() + 
"/test/jsp/generator/nonstandard/remove-01.jsp", body, null);
+        Assert.assertEquals("\n\n\n\n\n\n\n"
+                + "pageContext value=null\n"
+                + "request value=testValue\n"
+                + "session value=testValue\n"
+                + "application value=testValue", body.toString());
+        body.recycle();
+        getUrl("http://localhost:"; + getPort() + 
"/test/jsp/generator/nonstandard/remove-02.jsp", body, null);
+        Assert.assertEquals("\n\n\n\n\n\n\n"
+                + "pageContext value=testValue\n"
+                + "request value=null\n"
+                + "session value=testValue\n"
+                + "application value=testValue", body.toString());
+        body.recycle();
+        getUrl("http://localhost:"; + getPort() + 
"/test/jsp/generator/nonstandard/remove-03.jsp", body, null);
+        Assert.assertEquals("\n\n\n\n\n\n\n"
+                + "pageContext value=testValue\n"
+                + "request value=testValue\n"
+                + "session value=null\n"
+                + "application value=testValue", body.toString());
+        body.recycle();
+        getUrl("http://localhost:"; + getPort() + 
"/test/jsp/generator/nonstandard/remove-04.jsp", body, null);
+        Assert.assertEquals("\n\n\n\n\n\n\n"
+                + "pageContext value=testValue\n"
+                + "request value=testValue\n"
+                + "session value=testValue\n"
+                + "application value=null", body.toString());
+        body.recycle();
+        getUrl("http://localhost:"; + getPort() + 
"/test/jsp/generator/nonstandard/remove-05.jsp", body, null);
+        Assert.assertEquals("\n\n\n\n\n\n\n"
+                + "pageContext value=null\n"
+                + "request value=null\n"
+                + "session value=null\n"
+                + "application value=null", body.toString());
+        body.recycle();
+    }
 }
diff --git a/test/org/apache/jasper/compiler/TestNonstandardTagPerformance.java 
b/test/org/apache/jasper/compiler/TestNonstandardTagPerformance.java
new file mode 100644
index 0000000000..11d74957f9
--- /dev/null
+++ b/test/org/apache/jasper/compiler/TestNonstandardTagPerformance.java
@@ -0,0 +1,148 @@
+/*
+ * 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.compiler;
+
+import jakarta.el.ELContext;
+import jakarta.el.ELManager;
+import jakarta.el.ExpressionFactory;
+import jakarta.servlet.jsp.PageContext;
+import jakarta.servlet.jsp.TesterPageContext;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import org.apache.jasper.runtime.JspRuntimeLibrary;
+
+public class TestNonstandardTagPerformance {
+    static final long NUM_ITERATIONS = 100000000L;
+
+    static final int NUM_TESTS = 5;
+
+    private ELContext elContext;
+
+    private ExpressionFactory factory;
+
+    private ELManager manager;
+    public PageContext pageContext;
+
+    public jakarta.el.ExpressionFactory _jsp_getExpressionFactory() {
+        return factory;
+    }
+
+    private void newCode(jakarta.servlet.jsp.PageContext _jspx_page_context) 
throws java.lang.Throwable {
+        JspRuntimeLibrary.nonstandardSetTag(_jspx_page_context, null, 
_jspx_page_context,
+                jakarta.servlet.jsp.PageContext.PAGE_SCOPE);
+        _jspx_page_context.setAttribute("groupName", new Object(), 
jakarta.servlet.jsp.PageContext.PAGE_SCOPE);
+    }
+
+    /**
+     * This code is stolen from a generated JSP using standard c:set logic, 
except that the value is replaced by "new
+     * Object()". This eliminates the EL cost from the benchmark so we can 
accurately focus on the c:set performance.
+     *
+     * @param _jspx_th_c_005fwhen_005f11 parent tag
+     * @param _jspx_page_context page context
+     * @return whether to continue execution
+     * @throws java.lang.Throwable unknown error
+     */
+    private boolean oldCode(jakarta.servlet.jsp.tagext.JspTag 
_jspx_th_c_005fwhen_005f11,
+            jakarta.servlet.jsp.PageContext _jspx_page_context) throws 
java.lang.Throwable {
+//        jakarta.servlet.jsp.PageContext pageContext = _jspx_page_context;
+//        jakarta.servlet.jsp.JspWriter out = _jspx_page_context.getOut();
+//        // c:set
+//        org.apache.taglibs.standard.tag.rt.core.SetTag 
_jspx_th_c_005fset_005f39 = new 
org.apache.taglibs.standard.tag.rt.core.SetTag();
+//        _jsp_getInstanceManager().newInstance(_jspx_th_c_005fset_005f39);
+//        try {
+//            _jspx_th_c_005fset_005f39.setPageContext(_jspx_page_context);
+//            
_jspx_th_c_005fset_005f39.setParent((jakarta.servlet.jsp.tagext.Tag) 
_jspx_th_c_005fwhen_005f11);
+//            // 
/WEB-INF/views/jsp/features/buybox/offerDisplayGroupLayout.jsp(230,12) name = 
var type = java.lang.String
+//            // reqTime = false required = false fragment = false 
deferredValue = false expectedTypeName = null
+//            // deferredMethod = false methodSignature = null
+//            _jspx_th_c_005fset_005f39.setVar("groupName");
+//            // 
/WEB-INF/views/jsp/features/buybox/offerDisplayGroupLayout.jsp(230,12) name = 
value type =
+//            // javax.el.ValueExpression reqTime = true required = false 
fragment = false deferredValue = true
+//            // expectedTypeName = java.lang.Object deferredMethod = false 
methodSignature = null
+//            _jspx_th_c_005fset_005f39.setValue(new Object());
+//            int _jspx_eval_c_005fset_005f39 = 
_jspx_th_c_005fset_005f39.doStartTag();
+//            if (_jspx_th_c_005fset_005f39.doEndTag() == 
jakarta.servlet.jsp.tagext.Tag.SKIP_PAGE) {
+//                return true;
+//            }
+//        } finally {
+//            
org.apache.jasper.runtime.JspRuntimeLibrary.releaseTag(_jspx_th_c_005fset_005f39,
+//                    _jsp_getInstanceManager());
+//        }
+        return false;
+    }
+
+    /**
+     * This method depends on the availability of 
org.apache.taglibs.standard.tag.rt.core.SetTag, which is not typically
+     * available for Tomcat's unit tests. To execute the benchmark correctly, 
please:
+     * <ol>
+     * <li>add a taglibs jar to the classpath</li>
+     * <li>uncomment the body of {@code oldCode()}</li>
+     * <li>run the test manually (IDE or command-line)</li>
+     * <li>use jvm args similar to
+     *
+     * <pre>
+     * -Xmx1g -Xms1g -verbose:gc -XX:+UseParallelGC
+     * </pre>
+     *
+     * </li>
+     * </ol>
+     * @throws Throwable generic error
+     */
+    @Ignore
+    @Test
+    public void runBenchmark() throws Throwable {
+        long[] durations = new long[NUM_TESTS];
+        for (int i = 0; i < NUM_ITERATIONS / 10; i++) {
+            oldCode(null, pageContext);
+            newCode(pageContext);
+        }
+
+        for (int i = 0; i < NUM_TESTS; i++) {
+            long start = System.currentTimeMillis();
+            for (long j = 0; j < NUM_ITERATIONS; j++) {
+                oldCode(null, pageContext);
+            }
+            durations[i] = System.currentTimeMillis() - start;
+        }
+
+        for (int d = 0; d < durations.length; d++) {
+            System.out.println("Old: " + d + ". " + durations[d] + "ms");
+        }
+        for (int i = 0; i < NUM_TESTS; i++) {
+            long start = System.currentTimeMillis();
+            for (long j = 0; j < NUM_ITERATIONS; j++) {
+                newCode(pageContext);
+            }
+            durations[i] = System.currentTimeMillis() - start;
+        }
+
+        for (int d = 0; d < durations.length; d++) {
+            System.out.println("New: " + d + ". " + durations[d] + "ms");
+        }
+    }
+
+    @Before
+    public void setupTestVars() {
+        manager = new ELManager();
+        elContext = manager.getELContext();
+        factory = ELManager.getExpressionFactory();
+        pageContext = new TesterPageContext(elContext);
+    }
+}
diff --git a/test/org/apache/jasper/runtime/TestJspRuntimeLibrary.java 
b/test/org/apache/jasper/runtime/TestJspRuntimeLibrary.java
index 7148e860d7..bf1469bfe8 100644
--- a/test/org/apache/jasper/runtime/TestJspRuntimeLibrary.java
+++ b/test/org/apache/jasper/runtime/TestJspRuntimeLibrary.java
@@ -16,7 +16,12 @@
  */
 package org.apache.jasper.runtime;
 
+import jakarta.el.ELManager;
+import jakarta.servlet.jsp.PageContext;
+import jakarta.servlet.jsp.TesterPageContextWithAttributes;
+
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 import org.apache.catalina.startup.TomcatBaseTest;
@@ -24,6 +29,8 @@ import org.apache.tomcat.util.buf.ByteChunk;
 
 public class TestJspRuntimeLibrary extends TomcatBaseTest {
 
+    private PageContext pageContext;
+
     /*
      * Tests successful conversions
      */
@@ -140,4 +147,34 @@ public class TestJspRuntimeLibrary extends TomcatBaseTest {
     private static void assertEcho(String result, String expected) {
         Assert.assertTrue(result, result.indexOf("<p>" + expected + "</p>") > 
0);
     }
+
+    @Before
+    public void setupTestVars() {
+        pageContext = new TesterPageContextWithAttributes((new 
ELManager()).getELContext());
+    }
+
+    @Test
+    public void testNonstandardSetWithUndefinedScope() {
+        JspRuntimeLibrary.nonstandardSetTag(pageContext, "var", "value", 
PageContext.PAGE_SCOPE);
+        Assert.assertEquals("value", pageContext.getAttribute("var"));
+        Assert.assertEquals("value", pageContext.getAttribute("var", 
PageContext.PAGE_SCOPE));
+        Assert.assertEquals(null, pageContext.getAttribute("var", 
PageContext.REQUEST_SCOPE));
+
+        JspRuntimeLibrary.nonstandardSetTag(pageContext, "var", null, 
PageContext.PAGE_SCOPE);
+        Assert.assertEquals(null, pageContext.getAttribute("var"));
+
+    }
+
+    @Test
+    public void testNonstandardSetWithDefinedScope() {
+        final int[] scopes = { PageContext.PAGE_SCOPE, 
PageContext.REQUEST_SCOPE, PageContext.SESSION_SCOPE,
+                PageContext.APPLICATION_SCOPE };
+        for (int scope : scopes) {
+            JspRuntimeLibrary.nonstandardSetTag(pageContext, "var", "value", 
scope);
+            Assert.assertEquals("value", pageContext.getAttribute("var", 
scope));
+
+            JspRuntimeLibrary.nonstandardSetTag(pageContext, "var", null, 
scope);
+            Assert.assertEquals(null, pageContext.getAttribute("var", scope));
+        }
+    }
 }
diff --git a/test/webapp/WEB-INF/web.xml b/test/webapp/WEB-INF/web.xml
index 43eb0f1137..ad387996d9 100644
--- a/test/webapp/WEB-INF/web.xml
+++ b/test/webapp/WEB-INF/web.xml
@@ -28,6 +28,11 @@
      required.
   </description>
 
+  <servlet-mapping>
+    <servlet-name>JSP-nonstandard</servlet-name>
+    <url-pattern>/jsp/generator/nonstandard/*</url-pattern>
+  </servlet-mapping>
+
   <servlet>
     <servlet-name>jsp</servlet-name>
       <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
@@ -47,6 +52,29 @@
     <load-on-startup>3</load-on-startup>
   </servlet>
 
+  <servlet>
+    <servlet-name>JSP-nonstandard</servlet-name>
+      <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+      <init-param>
+        <param-name>fork</param-name>
+        <param-value>false</param-value>
+      </init-param>
+      <init-param>
+        <param-name>xpoweredBy</param-name>
+        <param-value>false</param-value>
+      </init-param>
+      <init-param>
+        <param-name>useNonstandardTagOptimizations</param-name>
+        <param-value>c:set,c:remove</param-value>
+      </init-param>
+      <!-- Required by /jsp/tagFileInJar.jsp / TestJspCompilationContext  -->
+      <init-param>
+        <param-name>modificationTestInterval</param-name>
+        <param-value>0</param-value>
+      </init-param>
+    <load-on-startup>3</load-on-startup>
+  </servlet>
+
   <!--  Bug 49922 -->
   <filter>
     <filter-name>Bug49922</filter-name>
diff --git a/test/webapp/jsp/generator/nonstandard/remove-01.jsp 
b/test/webapp/jsp/generator/nonstandard/remove-01.jsp
new file mode 100644
index 0000000000..cc2068c5a9
--- /dev/null
+++ b/test/webapp/jsp/generator/nonstandard/remove-01.jsp
@@ -0,0 +1,26 @@
+<%--
+ 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://java.sun.com/jsp/jstl/core"; prefix = "c" %>
+<c:set var="testVar" value="${'testValue'}" scope="page"/>
+<c:set var="testVar" value="${'testValue'}" scope="request"/>
+<c:set var="testVar" value="${'testValue'}" scope="session"/>
+<c:set var="testVar" value="${'testValue'}" scope="application"/>
+<c:remove var="testVar" scope="page"/>
+pageContext value=<%= pageContext.getAttribute("testVar") %>
+request value=<%= request.getAttribute("testVar") %>
+session value=<%= session.getAttribute("testVar") %>
+application value=<%= application.getAttribute("testVar") %>
\ No newline at end of file
diff --git a/test/webapp/jsp/generator/nonstandard/remove-02.jsp 
b/test/webapp/jsp/generator/nonstandard/remove-02.jsp
new file mode 100644
index 0000000000..74ba79d63f
--- /dev/null
+++ b/test/webapp/jsp/generator/nonstandard/remove-02.jsp
@@ -0,0 +1,26 @@
+<%--
+ 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://java.sun.com/jsp/jstl/core"; prefix = "c" %>
+<c:set var="testVar" value="${'testValue'}" scope="page"/>
+<c:set var="testVar" value="${'testValue'}" scope="request"/>
+<c:set var="testVar" value="${'testValue'}" scope="session"/>
+<c:set var="testVar" value="${'testValue'}" scope="application"/>
+<c:remove var="testVar" scope="request"/>
+pageContext value=<%= pageContext.getAttribute("testVar") %>
+request value=<%= request.getAttribute("testVar") %>
+session value=<%= session.getAttribute("testVar") %>
+application value=<%= application.getAttribute("testVar") %>
\ No newline at end of file
diff --git a/test/webapp/jsp/generator/nonstandard/remove-03.jsp 
b/test/webapp/jsp/generator/nonstandard/remove-03.jsp
new file mode 100644
index 0000000000..2aa90840c9
--- /dev/null
+++ b/test/webapp/jsp/generator/nonstandard/remove-03.jsp
@@ -0,0 +1,26 @@
+<%--
+ 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://java.sun.com/jsp/jstl/core"; prefix = "c" %>
+<c:set var="testVar" value="${'testValue'}" scope="page"/>
+<c:set var="testVar" value="${'testValue'}" scope="request"/>
+<c:set var="testVar" value="${'testValue'}" scope="session"/>
+<c:set var="testVar" value="${'testValue'}" scope="application"/>
+<c:remove var="testVar" scope="session"/>
+pageContext value=<%= pageContext.getAttribute("testVar") %>
+request value=<%= request.getAttribute("testVar") %>
+session value=<%= session.getAttribute("testVar") %>
+application value=<%= application.getAttribute("testVar") %>
\ No newline at end of file
diff --git a/test/webapp/jsp/generator/nonstandard/remove-04.jsp 
b/test/webapp/jsp/generator/nonstandard/remove-04.jsp
new file mode 100644
index 0000000000..0fe5ff2f03
--- /dev/null
+++ b/test/webapp/jsp/generator/nonstandard/remove-04.jsp
@@ -0,0 +1,26 @@
+<%--
+ 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://java.sun.com/jsp/jstl/core"; prefix = "c" %>
+<c:set var="testVar" value="${'testValue'}" scope="page"/>
+<c:set var="testVar" value="${'testValue'}" scope="request"/>
+<c:set var="testVar" value="${'testValue'}" scope="session"/>
+<c:set var="testVar" value="${'testValue'}" scope="application"/>
+<c:remove var="testVar" scope="application"/>
+pageContext value=<%= pageContext.getAttribute("testVar") %>
+request value=<%= request.getAttribute("testVar") %>
+session value=<%= session.getAttribute("testVar") %>
+application value=<%= application.getAttribute("testVar") %>
\ No newline at end of file
diff --git a/test/webapp/jsp/generator/nonstandard/remove-05.jsp 
b/test/webapp/jsp/generator/nonstandard/remove-05.jsp
new file mode 100644
index 0000000000..781a91b1fe
--- /dev/null
+++ b/test/webapp/jsp/generator/nonstandard/remove-05.jsp
@@ -0,0 +1,26 @@
+<%--
+ 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://java.sun.com/jsp/jstl/core"; prefix = "c" %>
+<c:set var="testVar" value="${'testValue'}" scope="page"/>
+<c:set var="testVar" value="${'testValue'}" scope="request"/>
+<c:set var="testVar" value="${'testValue'}" scope="session"/>
+<c:set var="testVar" value="${'testValue'}" scope="application"/>
+<c:remove var="testVar"/>
+pageContext value=<%= pageContext.getAttribute("testVar") %>
+request value=<%= request.getAttribute("testVar") %>
+session value=<%= session.getAttribute("testVar") %>
+application value=<%= application.getAttribute("testVar") %>
\ No newline at end of file
diff --git a/test/webapp/jsp/generator/nonstandard/set-01.jsp 
b/test/webapp/jsp/generator/nonstandard/set-01.jsp
new file mode 100644
index 0000000000..da6e45d0d4
--- /dev/null
+++ b/test/webapp/jsp/generator/nonstandard/set-01.jsp
@@ -0,0 +1,22 @@
+<%--
+ 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://java.sun.com/jsp/jstl/core"; prefix = "c" %>
+<c:set var="testVar" value="${'testValue'}"/>
+pageContext value=<%= pageContext.getAttribute("testVar") %>
+request value=<%= request.getAttribute("testVar") %>
+session value=<%= session.getAttribute("testVar") %>
+application value=<%= application.getAttribute("testVar") %>
\ No newline at end of file
diff --git a/test/webapp/jsp/generator/nonstandard/set-02.jsp 
b/test/webapp/jsp/generator/nonstandard/set-02.jsp
new file mode 100644
index 0000000000..b944135491
--- /dev/null
+++ b/test/webapp/jsp/generator/nonstandard/set-02.jsp
@@ -0,0 +1,22 @@
+<%--
+ 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://java.sun.com/jsp/jstl/core"; prefix = "c" %>
+<c:set var="testVar" value="${'testValue'}" scope="page"/>
+pageContext value=<%= pageContext.getAttribute("testVar") %>
+request value=<%= request.getAttribute("testVar") %>
+session value=<%= session.getAttribute("testVar") %>
+application value=<%= application.getAttribute("testVar") %>
\ No newline at end of file
diff --git a/test/webapp/jsp/generator/nonstandard/set-03.jsp 
b/test/webapp/jsp/generator/nonstandard/set-03.jsp
new file mode 100644
index 0000000000..7652061487
--- /dev/null
+++ b/test/webapp/jsp/generator/nonstandard/set-03.jsp
@@ -0,0 +1,22 @@
+<%--
+ 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://java.sun.com/jsp/jstl/core"; prefix = "c" %>
+<c:set var="testVar" value="${'testValue'}" scope="request"/>
+pageContext value=<%= pageContext.getAttribute("testVar") %>
+request value=<%= request.getAttribute("testVar") %>
+session value=<%= session.getAttribute("testVar") %>
+application value=<%= application.getAttribute("testVar") %>
\ No newline at end of file
diff --git a/test/webapp/jsp/generator/nonstandard/set-04.jsp 
b/test/webapp/jsp/generator/nonstandard/set-04.jsp
new file mode 100644
index 0000000000..9f10765dc6
--- /dev/null
+++ b/test/webapp/jsp/generator/nonstandard/set-04.jsp
@@ -0,0 +1,22 @@
+<%--
+ 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://java.sun.com/jsp/jstl/core"; prefix = "c" %>
+<c:set var="testVar" value="${'testValue'}" scope="session"/>
+pageContext value=<%= pageContext.getAttribute("testVar") %>
+request value=<%= request.getAttribute("testVar") %>
+session value=<%= session.getAttribute("testVar") %>
+application value=<%= application.getAttribute("testVar") %>
\ No newline at end of file
diff --git a/test/webapp/jsp/generator/nonstandard/set-05.jsp 
b/test/webapp/jsp/generator/nonstandard/set-05.jsp
new file mode 100644
index 0000000000..8b41d435f6
--- /dev/null
+++ b/test/webapp/jsp/generator/nonstandard/set-05.jsp
@@ -0,0 +1,22 @@
+<%--
+ 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://java.sun.com/jsp/jstl/core"; prefix = "c" %>
+<c:set var="testVar" value="${'testValue'}" scope="application"/>
+pageContext value=<%= pageContext.getAttribute("testVar") %>
+request value=<%= request.getAttribute("testVar") %>
+session value=<%= session.getAttribute("testVar") %>
+application value=<%= application.getAttribute("testVar") %>
\ No newline at end of file
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 4424d5e404..c5584c9fe0 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -154,6 +154,11 @@
         <bug>69635</bug>: Add support to <code>jakarta.el.ImportHandler</code>
         for resolving inner classes. (markt)
       </fix>
+      <add>
+        <pr>842</pr>Add support for optimized execution of c:set and c:remove 
tags, when
+        activated via JSP servlet param useNonstandardTagOptimizations.
+        (jengebr)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Other">


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

Reply via email to