jengebr commented on code in PR #842: URL: https://github.com/apache/tomcat/pull/842#discussion_r2046881926
########## java/org/apache/jasper/compiler/Generator.java: ########## @@ -3028,6 +3036,195 @@ public String generateNamedAttributeJspFragment(Node.NamedAttribute n, String ta 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 + * @param jspAttributes + * @return whether code was generated + * @throws JasperException + */ + private boolean visitPotentiallyNonstandardCustomTag(Node.CustomTag n) + throws JasperException { + if (!nonstandardCustomTagNames.contains(n.getQName())) { + // tag is not configured, move along + return false; + } + + 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. + } + 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(); + String scopeValue = translateScopeToConstant(jspAttributes); + String evaluatedAttribute = evaluateAttribute(getTagHandlerInfo(n), valueAttribute, + n, null); + 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; + } + + 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; + } + + 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) { + out.printil("pageContext.removeAttribute(\"" + varValue + "\");"); + } else { + String scopeValue = translateScopeToConstant(jspAttributes); + out.printil("pageContext.removeAttribute(\"" + varValue + "\", " + scopeValue + ");"); + } Review Comment: Weird as this is, I think it matches the logic from [RemoveTag](https://github.com/javaee/jstl-api/blob/master/impl/src/main/java/org/apache/taglibs/standard/tag/common/core/RemoveTag.java): ``` // removes the variable (from a specific scope, if specified) public int doEndTag() throws JspException { if (!scopeSpecified) pageContext.removeAttribute(var); else pageContext.removeAttribute(var, scope); return EVAL_PAGE; } ``` And PageContextImpl .remove(var) removes from every scope, whereas .remove(var, scope) only removes from one:. ``` @Override public void removeAttribute(final String name) { if (name == null) { throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name")); } removeAttribute(name, PAGE_SCOPE); removeAttribute(name, REQUEST_SCOPE); if (session != null) { try { removeAttribute(name, SESSION_SCOPE); } catch (IllegalStateException ise) { // Session has been invalidated. // Ignore and fall throw to application scope. } } removeAttribute(name, APPLICATION_SCOPE); } ``` Until this effort, I didn't know that remove w/o scope cleared all scopes. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org