String concatenation tweaks

Aleksey Shipilev aleksey.shipilev at oracle.com
Thu Jun 4 09:47:53 UTC 2015


Hi Peter,

(I realized this was left unreplied)

On 06/02/2015 02:04 AM, Peter Levart wrote:
> String concatenation actually needs just two reference types (maybe
> three if you want to optimize for the literal null as a special type
> which is really a very rare case in practice). The JLS says that '+'
> binary operator where at least one of the arguments is of String type
> is string concatenation operator. If the other argument is not of
> String type then it must be converted to String type via String
> conversion:
> 
> https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.11

> Which says that .toString() needs to be called if the argument is a
> reference type other than String and not null at runtime. I checked
> javac and yes, it emits code which only ever invokes two of the
> overloaded StringBuilder.append() methods for reference type
> arguments:
> 
> append(String); append(Object);
> 
> so the following expression:
> 
> "a" + new StringBuffer("b")
> 
> ...is actually translated to equivalent of:
> 
> new StringBuilder().append("a").append((Object) new
> StringBuffer("b")).toString()

Notice that even now javac is not following the JLS to the exact letter.
If you interpret the JLS wording as the implementation requirement, you
would expect javac to instead emit:

new StringBuilder()
 .append("a")
 .append((new StringBuffer("b").toString())
 .toString()

...and basically use only the StringBuilder::append(String) everywhere.
StringBuilder::append(Object) *and* StringBuilder::append(<primitive>)
should be out of the question, if you treat the JLS semantic guidance as
the only implementation. But javac does not do it, and this is fine,
because...


> Does this subtle detail need to be respected or can dirty
> tricks be played with known types to "optimize" their conversion to
> String without actually invoking .toString() or even creating an
> intermediary String object? Also the String Conversion specification
> says that in case the .toString() method returns null, then "null"
> string is a result of such String conversion. So there have to be 2 null
> checks for reference arguments - before and after .toString() call.

...there is a long way from what spec requires "to behave like", and
what is actually done by the implementation. C2's OptimizeStringConcat,
for example, does not care about toString() calls anymore, it has its
own idea how to convert arguments to Strings. Until users can detect the
semantics deviation, I think we are fine.


> Also I don't think string concatenation will ever want to know the
> precise compile-time types of the reference typed arguments apart from
> the three mentioned: String, Object, null, unless it wants to play dirty
> tricks with some types to "optimize" String conversion (like
> StringBuilder.append(StringBuffer) does for example).

Future-proof basically means that we don't preclude optimizations we are
dumb to come up with today. The absence of evidence is not the evidence
of absence. It would be much safer to leave the exact types in the
generated bytecode and have no optimizations to leverage them yet, than
to strip the types out, and realize you need them for some future
optimization to work. Contingencies!


>> Example case: would it make sense to null-check and unbox Integer before
>> pushing it on to append() chain? This will set us up for
>> OptoStringConcat for new SB().append(String).append(Integer).toString() :)
> 
> What is Integer at compile time can always be null at runtime:
> 
> Integer i = null;
> 
> System.out.println("i=" + i);

Yes, but a funny part is that you can null-check it early, replace it
with "null" if something goes wrong; unbox the Integer on a "happy
path", and employ the in-place int->char[] conversion.

Granted, you can emit the (arg instanceof Integer) if Integer was
stripped down to opaque Object by javac, but why doing this if we can
allow the exact types communicated to us by javac itself?


Thanks,
-Aleksey


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: OpenPGP digital signature
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20150604/76a80323/signature.asc>


More information about the compiler-dev mailing list