Bug 50726 - Jasper can generate uncompilable source code if genStringAsCharArray is turned on
Jasper can generate uncompilable source code if genStringAsCharArray is turne...
Status: RESOLVED FIXED
Product: Tomcat 6
Classification: Unclassified
Component: Jasper
6.0.28
All All
: P2 normal (vote)
: default
Assigned To: Tomcat Developers Mailing List
:
Depends on:
Blocks:
  Show dependency tree
 
Reported: 2011-02-07 08:14 UTC by Róbert István
Modified: 2011-06-03 07:31 UTC (History)
0 users



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Róbert István 2011-02-07 08:14:03 UTC
Our jsp pages are compiled at build time. If I turn on genStringAsCharArray optimization switch, in some cases there will be longer lines in the generated java code than the javac compiler can process. It provides the following error message:

[javac] Compiling 1609 source files to d:\...\jspc\classes
[javac] d:\...\jspc\src\org\apache\jsp\WEB_002dINF\..._005fAPP\pages\termsconditions\TermsAndConditions_005fms_005fMY_jsp.java:96: constant string too long
Comment 1 Konstantin Kolinko 2011-02-08 03:17:35 UTC
The genStringAsCharArray option affects the o.a.jasper.compiler.Generator.GenerateVisitor#visit(Node.TemplateText) method.

It generates char array fields in the JSP page class initialized as
field = "string".toCharArray();

The "string" constants have 64K limit on string length. It is not run-time limitation, but a limitation of the Java Class File format, see [1]. Apparently it is what is being hit here.

[1] http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#88659
Comment 2 Róbert István 2011-02-08 03:55:04 UTC
(In reply to comment #1)
> The genStringAsCharArray option affects the
> o.a.jasper.compiler.Generator.GenerateVisitor#visit(Node.TemplateText) method.
> 
> It generates char array fields in the JSP page class initialized as
> field = "string".toCharArray();
> 
> The "string" constants have 64K limit on string length. It is not run-time
> limitation, but a limitation of the Java Class File format, see [1]. Apparently
> it is what is being hit here.
> 
> [1]
> http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#88659

Thank you, it's clear for me now. Is it be possible to provide an official workaround like this?
 field = (new String("longstring_part1") + 
    new String("longstring_part2") +
    ...
    new String("longstring_partn")).toCharArray();
Comment 3 Konstantin Kolinko 2011-02-08 04:15:35 UTC
>  field = (new String("longstring_part1") + 
>     new String("longstring_part2") +
>     ...
>     new String("longstring_partn")).toCharArray();

I think that wouldn't work. What will work is to generate several arrays and call out.write() with each one in turn.

Note that the 64K limit is not the char count aka str.length(), but the count of bytes in UTF-8 encoding.  So, either that count should be calculated (better without String.toBytes() call) or the threshold should be like (64K / (max count of bytes in char in UTF-8)).

I wonder also whether initializing the array as = {'s', 't', 'r', 'i', 'n', 'g'} performs worse or better than the existing code.

Anyway, a test case is needed.


BTW, using mappedFile=false will probably hit the same limit.
(That is when the "breakAtLF" variable is false in that visit(Node.TemplateText) method).
Comment 4 Mark Thomas 2011-02-11 13:03:45 UTC
For info, it appears the the Eclipse compiler does not enforce this limit. Dynamic compilation works whereas pre-compilation fails.
Comment 5 Mark Thomas 2011-02-12 14:37:44 UTC
I have fixed this in 7.0.x and it will be included in 7.0.9 onwards.

I have also proposed the fix for 6.0.x
Comment 6 Konstantin Kolinko 2011-02-14 11:12:26 UTC
(In reply to comment #4)
> For info, it appears the the Eclipse compiler does not enforce this limit.
> Dynamic compilation works whereas pre-compilation fails.

That would be strange. It is class format limitation: the text will be broken if the length counter overflows.

There is caveat with this setting: the init-param is named "genStrAsCharArray" (see comment in web.xml), not genStringAsCharArray as the property in the Options interface, or as said in the title of this bug report. The mapping between names is done inside the EmbeddedServletOptions class constructor.
Comment 7 Konstantin Kolinko 2011-02-26 11:28:24 UTC
Answering my own questions

(In reply to comment #3)
> I wonder also whether initializing the array as = {'s', 't', 'r', 'i', 'n',
> 'g'} performs worse or better than the existing code.

It works, but generates a lot of code, like the following (for an array of 11 chars):
   0:	bipush	11
   2:	newarray char
   4:	dup
   5:	iconst_0
   6:	bipush	72
   8:	castore
   9:	dup
   10:	iconst_1
   11:	bipush	101
   13:	castore
   14:	dup
(...)
   59:	bipush	10
   61:	bipush	100
   63:	castore
   64:	putstatic
   67:	return

I have not measured whether it works considerably slow or not.

> BTW, using mappedFile=false will probably hit the same limit.
> (That is when the "breakAtLF" variable is false in that
> visit(Node.TemplateText) method).

The mappedFile=false will not experience this issue under usual circumstances.
 
There is a constant JspUtil.CHUNKSIZE and thus strings longer than 1024 will be split at LF regardless of breakAtLF flag. If the JSP has lines of 64K bytes without any LF in them, this issue will be observed regardless of mappedFile setting. But such JSPs are very unlikely.
Comment 8 Mark Thomas 2011-06-03 07:31:01 UTC
Fixed in 6.0.x and will be included in 6.0.33 onwards.