-
Yes, correct, anything above Java 1.4 should be using StringBuilder (and it’s what javac compiles any “This is something” + “Something Else” statement) . Hence I included both cases.
-
Also correct, I’m talking about the runtime phase, because that’s where the performance that matters is - since compilation should happen once. I was using writeOutput just so it was more understandable, but yes, you’re right, the full statement would be more like the response.getOutputStream.write() you’ve written.
I submit that the performance benefit is negligible for this specific use case because it’s so tiny and the RAM usage is minimal. Most of the times when this would matter can be replaced with cfcontent file=“something”. (Because spraying the network stream with a 100MB PDF IS noticeable in both memory and CPU cycles) BY FAR the network output part will be slower than the “writing to memory” part.
While I agree in theory, you’re talking about nanoseconds. In fact, the JVM hotspot compiler might even optimize the variable out, leaving you with almost exactly the same bytecode.
I think it’d be hard to find a case when the tag island were so big as to cause any sort of measurable performance impact.
So let’s look at other reasons:
- Script tags are independently compiled (as per @micstriit )… Since this wouldn’t change, some of the examples you’ve posted wouldn’t work… Because having a tag island within a for would break things. (because the open and close braces are in different cfscript contexts) So it can’t be syntactically equivalent as you’ve indicated, without some other hoops. (see post 79 https://lucee.daemonite.io/t/syntax-for-html-island-in-script/2347/79)
It seems the only way to go switching back and forth is for the page to be processed in multiple passes based on the primary parser. Right now that’s CFML, unless it’s a script CFC. You’d have to parse the entire CFML, with placeholders for the cfscript bits it can’t understand. Then go back and parse those cfscript bits, and put them in the compilation in the appropriate place. Or as Micha said, basically each parser treats the other language as a string.
If CF is going to be smart enough to point out the mismatched braces it would have to treat multiple script blocks as 1 unit.
This seems really really hard to do. Not saying it’s not a good idea, but cost/benefit is a real thing.
I know you’re not looking for string eval, but treating ‘’’ thru ‘’’ as string delimiters eliminates that problem, because cfscript can easily find the beginning and end.
I’d actually prefer the result end up in a string, because then it gives more options - pass into QueryExecute for instance. I don’t think it feasible to change the functionality based on context.
If ''' is equivalent to </cfscript> block </cfscript>, then queryexecute(</cfscript>blah<cfscript>) makes no sense.
IMHO, it should either always return a string, or always do straight output - and of those two options, always returning a string gives far more flexibility. Like passing the result into a moustache parser. Or a GroovyScriptEngine object. Or anything else.
Which also really just means it’s a language markup equivalent of cfsavecontent
with the “variable” being the return value.
- Migration issues - will this actually help? TL;DR probably not.
I think most CFC conversions will start with:
<cfcomponent>
<cfscript>
nothing in script
</cfscript>
everything not yet migrated
</cfcomponent>
As time goes on, stuff moves from the tagged area to the scripted area, until the cfscript can be removed and the file can go component {} natively.
I don’t know that having a tag island makes this job any easier/better.
Note that personally I like cfquery way better than queryExecute, and we have a lot of good reasons to go that route here. I don’t see that changing. Thankfully, I have the option to use tagged CFCs to make that possible in those specific scenarios. And that doesn’t make me a 5 tagger. But I’d rather have a tagged CFC than a tag-island for each cfquery.
- Ability to render output - largely removing quote mismatch issues
I see the merit here - having to do writeOutput("html");
means you can’t use " anywhere in the string literal, it has to be escaped as #Chr(34)#
or some other option. This IS a pain point. Being able to do writeOutput(''' whatever I want ''');
does make sense to me from a string parsing perspective.
But I think the original examples also make sense in that cfoutput should be explicitly on. Basically, this doesn’t sound like a tag island at all to me - it sounds like a multi-line string solution, and were we to put this string in WriteOutput or in cfset, it would evaluate hash variables.
Much like groovy:
It means for HTML output you explicitly add the writeoutput()
, for queryExecute
you explicitly put that in, for other languages, you return the result or you execute a parser.
It might also be worth adding a cfoutput=“yes” option to cfsavecontent - which eliminates the need to have a cfoutput in your block.
I get from a java perspective writing to the output stream directly makes sense from an efficiency perspective, I’m just having a hard time imagining a case when it would be noticeable… and I think the benefits of multi-line strings outweigh the possible performance issue.
I think this is in line with what @markdrew’s examples were illustrating and eliminates the possible concerns from @ddspringle, because multi-line strings wouldn’t evaluate <cfloop>
, just like cfset doesn’t. (Basically it’d use the expression parser, NOT the CFML parser)
I’m also a fan of @markdrew’s include with output variable or include returning a string (i.e. include like a function).
And even though it’d be cool I’m willing to accept that parsing other languages directly (like moustache, java, groovy) at the Lucee compilation level is unacceptable scope creep. (especially given how easy it’d be to pass multi-line strings into a function)
However, I believe it’s also possible to have some contextual logic - though I’d defer to @micstriit and @Igal… as an optimization, if the cfscript parser sees writeoutput(‘’‘some big string’‘’); then instead of dumping in a string buffer, it could write to the stream. That would provide an optimization for stream output without losing the flexibility and benefits of everything else, and essentially just be a mode toggle within the multiline-script parser/expression parser.
And just FYI, based on the source I believe
- cfsavecontent calls getBody() after the tag executes - so, big string buffer.
- render calls the Lucee Renderer directly, and supports a dialect, which right now needs to be a CFML or Lucee dialect. (Which I do not believe can be extended through extensions)
- cfoutput uses tag visitation to output directly to the stream. (not surprising)