# HG changeset patch # User shade # Date 1453999366 -10800 # Node ID 96661d1df628989b014eedd99866e91b50136714 # Parent 36c7b4ec7a8b2e5b3739ce7b1530873a7785f446 8148483: JEP 280: Indify String Concatenation Reviewed-by: psandoz, mcimadamore, igerasim, forax, plevart, vlivanov, ihse Contributed-by: Aleksey Shipilev , Remi Forax , Peter Levart diff -r 36c7b4ec7a8b -r 96661d1df628 langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java Thu Jan 28 14:06:27 2016 +0000 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java Thu Jan 28 19:42:46 2016 +0300 @@ -26,9 +26,7 @@ package com.sun.tools.javac.code; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import javax.lang.model.element.ElementVisitor; import javax.tools.JavaFileObject; @@ -39,7 +37,6 @@ import com.sun.tools.javac.code.Symbol.Completer; import com.sun.tools.javac.code.Symbol.CompletionFailure; import com.sun.tools.javac.code.Symbol.MethodSymbol; -import com.sun.tools.javac.code.Symbol.OperatorSymbol; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; @@ -50,7 +47,6 @@ import com.sun.tools.javac.code.Type.JCVoidType; import com.sun.tools.javac.code.Type.MethodType; import com.sun.tools.javac.code.Type.UnknownType; -import com.sun.tools.javac.jvm.ByteCodes; import com.sun.tools.javac.jvm.Target; import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; @@ -65,7 +61,6 @@ import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Kinds.Kind.*; -import static com.sun.tools.javac.jvm.ByteCodes.*; import static com.sun.tools.javac.code.TypeTag.*; /** A class that defines all predefined constants and operators @@ -193,6 +188,7 @@ public final Type autoCloseableType; public final Type trustMeType; public final Type lambdaMetafactory; + public final Type stringConcatFactory; public final Type repeatableType; public final Type documentedType; public final Type elementTypeType; @@ -472,6 +468,7 @@ trustMeType = enterClass("java.lang.SafeVarargs"); nativeHeaderType = enterClass("java.lang.annotation.Native"); lambdaMetafactory = enterClass("java.lang.invoke.LambdaMetafactory"); + stringConcatFactory = enterClass("java.lang.invoke.StringConcatFactory"); functionalInterfaceType = enterClass("java.lang.FunctionalInterface"); synthesizeEmptyInterfaceIfMissing(autoCloseableType); @@ -479,6 +476,7 @@ synthesizeEmptyInterfaceIfMissing(serializableType); synthesizeEmptyInterfaceIfMissing(lambdaMetafactory); synthesizeEmptyInterfaceIfMissing(serializedLambdaType); + synthesizeEmptyInterfaceIfMissing(stringConcatFactory); synthesizeBoxTypeIfMissing(doubleType); synthesizeBoxTypeIfMissing(floatType); synthesizeBoxTypeIfMissing(voidType); diff -r 36c7b4ec7a8b -r 96661d1df628 langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Thu Jan 28 14:06:27 2016 +0000 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java Thu Jan 28 19:42:46 2016 +0300 @@ -25,8 +25,6 @@ package com.sun.tools.javac.jvm; -import java.util.*; - import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.List; @@ -45,7 +43,6 @@ import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Kinds.Kind.*; -import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.jvm.ByteCodes.*; import static com.sun.tools.javac.jvm.CRTFlags.*; @@ -69,12 +66,12 @@ private final TreeMaker make; private final Names names; private final Target target; - private final Map stringBufferAppend; private Name accessDollar; private final Types types; private final Lower lower; private final Flow flow; private final Annotate annotate; + private final StringConcat concat; /** Format of stackmap tables to be generated. */ private final Code.StackMapFormat stackMap; @@ -105,8 +102,9 @@ make = TreeMaker.instance(context); target = Target.instance(context); types = Types.instance(context); + concat = StringConcat.instance(context); + methodType = new MethodType(null, null, null, syms.methodClass); - stringBufferAppend = new HashMap<>(); accessDollar = names. fromString("access" + target.syntheticNameChar()); flow = Flow.instance(context); @@ -753,6 +751,18 @@ } } + public Code getCode() { + return code; + } + + public Items getItems() { + return items; + } + + public Env getAttrEnv() { + return attrEnv; + } + /** Visitor class for expressions which might be constant expressions. * This class is a subset of TreeScanner. Intended to visit trees pruned by * Lower as long as constant expressions looking for references to any @@ -1895,25 +1905,7 @@ OperatorSymbol operator = (OperatorSymbol) tree.operator; Item l; if (operator.opcode == string_add) { - // Generate code to make a string buffer - makeStringBuffer(tree.pos()); - - // Generate code for first string, possibly save one - // copy under buffer - l = genExpr(tree.lhs, tree.lhs.type); - if (l.width() > 0) { - code.emitop0(dup_x1 + 3 * (l.width() - 1)); - } - - // Load first string and append to buffer. - l.load(); - appendString(tree.lhs); - - // Append all other strings to buffer. - appendStrings(tree.rhs); - - // Convert buffer to string. - bufferToString(tree.pos()); + l = concat.makeConcat(tree); } else { // Generate code for first expression l = genExpr(tree.lhs, tree.lhs.type); @@ -2026,13 +2018,7 @@ public void visitBinary(JCBinary tree) { OperatorSymbol operator = (OperatorSymbol)tree.operator; if (operator.opcode == string_add) { - // Create a string buffer. - makeStringBuffer(tree.pos()); - // Append all strings to buffer. - appendStrings(tree); - // Convert buffer to string. - bufferToString(tree.pos()); - result = items.makeStackItem(syms.stringType); + result = concat.makeConcat(tree); } else if (tree.hasTag(AND)) { CondItem lcond = genCond(tree.lhs, CRT_FLOW_CONTROLLER); if (!lcond.isFalse()) { @@ -2066,67 +2052,7 @@ result = completeBinop(tree.lhs, tree.rhs, operator); } } -//where - /** Make a new string buffer. - */ - void makeStringBuffer(DiagnosticPosition pos) { - code.emitop2(new_, makeRef(pos, syms.stringBuilderType)); - code.emitop0(dup); - callMethod( - pos, syms.stringBuilderType, names.init, List.nil(), false); - } - /** Append value (on tos) to string buffer (on tos - 1). - */ - void appendString(JCTree tree) { - Type t = tree.type.baseType(); - if (!t.isPrimitive() && t.tsym != syms.stringType.tsym) { - t = syms.objectType; - } - items.makeMemberItem(getStringBufferAppend(tree, t), false).invoke(); - } - Symbol getStringBufferAppend(JCTree tree, Type t) { - Assert.checkNull(t.constValue()); - Symbol method = stringBufferAppend.get(t); - if (method == null) { - method = rs.resolveInternalMethod(tree.pos(), - attrEnv, - syms.stringBuilderType, - names.append, - List.of(t), - null); - stringBufferAppend.put(t, method); - } - return method; - } - - /** Add all strings in tree to string buffer. - */ - void appendStrings(JCTree tree) { - tree = TreeInfo.skipParens(tree); - if (tree.hasTag(PLUS) && tree.type.constValue() == null) { - JCBinary op = (JCBinary) tree; - if (op.operator.kind == MTH && - ((OperatorSymbol) op.operator).opcode == string_add) { - appendStrings(op.lhs); - appendStrings(op.rhs); - return; - } - } - genExpr(tree, tree.type).load(); - appendString(tree); - } - - /** Convert string buffer on tos to string. - */ - void bufferToString(DiagnosticPosition pos) { - callMethod( - pos, - syms.stringBuilderType, - names.toString, - List.nil(), - false); - } /** Complete generating code for operation, with left operand * already on stack. diff -r 36c7b4ec7a8b -r 96661d1df628 langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/StringConcat.java Thu Jan 28 19:42:46 2016 +0300 @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javac.jvm; + +import com.sun.tools.javac.code.*; +import com.sun.tools.javac.comp.Resolve; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.util.*; + +import static com.sun.tools.javac.code.Kinds.Kind.MTH; +import static com.sun.tools.javac.code.TypeTag.DOUBLE; +import static com.sun.tools.javac.code.TypeTag.LONG; +import static com.sun.tools.javac.jvm.ByteCodes.*; +import static com.sun.tools.javac.tree.JCTree.Tag.PLUS; +import com.sun.tools.javac.jvm.Items.*; + +import java.util.HashMap; +import java.util.Map; + +/** This lowers the String concatenation to something that JVM can understand. + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public abstract class StringConcat { + + /** + * Maximum number of slots for String Concat call. + * JDK's StringConcatFactory does not support more than that. + */ + private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200; + private static final char TAG_ARG = '\u0001'; + private static final char TAG_CONST = '\u0002'; + + protected final Gen gen; + protected final Symtab syms; + protected final Names names; + protected final TreeMaker make; + protected final Types types; + protected final Map sbAppends; + protected final Resolve rs; + + protected static final Context.Key concatKey = new Context.Key<>(); + + public static StringConcat instance(Context context) { + StringConcat instance = context.get(concatKey); + if (instance == null) { + instance = makeConcat(context); + } + return instance; + } + + private static StringConcat makeConcat(Context context) { + Target target = Target.instance(context); + String opt = Options.instance(context).get("stringConcat"); + if (target.hasStringConcatFactory()) { + if (opt == null) { + opt = "indyWithConstants"; + } + } else { + if (opt != null && !"inline".equals(opt)) { + Assert.error("StringConcatFactory-based string concat is requested on a platform that does not support it."); + } + opt = "inline"; + } + + switch (opt) { + case "inline": + return new Inline(context); + case "indy": + return new IndyPlain(context); + case "indyWithConstants": + return new IndyConstants(context); + default: + Assert.error("Unknown stringConcat: " + opt); + throw new IllegalStateException("Unknown stringConcat: " + opt); + } + } + + protected StringConcat(Context context) { + context.put(concatKey, this); + gen = Gen.instance(context); + syms = Symtab.instance(context); + types = Types.instance(context); + names = Names.instance(context); + make = TreeMaker.instance(context); + rs = Resolve.instance(context); + sbAppends = new HashMap<>(); + } + + public abstract Item makeConcat(JCTree.JCAssignOp tree); + public abstract Item makeConcat(JCTree.JCBinary tree); + + protected List collectAll(JCTree tree) { + return collect(tree, List.nil()); + } + + protected List collectAll(JCTree.JCExpression lhs, JCTree.JCExpression rhs) { + return List.nil() + .appendList(collectAll(lhs)) + .appendList(collectAll(rhs)); + } + + private List collect(JCTree tree, List res) { + tree = TreeInfo.skipParens(tree); + if (tree.hasTag(PLUS) && tree.type.constValue() == null) { + JCTree.JCBinary op = (JCTree.JCBinary) tree; + if (op.operator.kind == MTH && + ((Symbol.OperatorSymbol) op.operator).opcode == string_add) { + return res + .appendList(collect(op.lhs, res)) + .appendList(collect(op.rhs, res)); + } + } + return res.append(tree); + } + + /** + * "Legacy" bytecode flavor: emit the StringBuilder.append chains for string + * concatenation. + */ + private static class Inline extends StringConcat { + public Inline(Context context) { + super(context); + } + + @Override + public Item makeConcat(JCTree.JCAssignOp tree) { + // Generate code to make a string builder + JCDiagnostic.DiagnosticPosition pos = tree.pos(); + + // Create a string builder. + newStringBuilder(tree); + + // Generate code for first string, possibly save one + // copy under builder + Item l = gen.genExpr(tree.lhs, tree.lhs.type); + if (l.width() > 0) { + gen.getCode().emitop0(dup_x1 + 3 * (l.width() - 1)); + } + + // Load first string and append to builder. + l.load(); + appendString(tree.lhs); + + // Append all other strings to builder. + List args = collectAll(tree.rhs); + for (JCTree t : args) { + gen.genExpr(t, t.type).load(); + appendString(t); + } + + // Convert builder to string. + builderToString(pos); + + return l; + } + + @Override + public Item makeConcat(JCTree.JCBinary tree) { + JCDiagnostic.DiagnosticPosition pos = tree.pos(); + + // Create a string builder. + newStringBuilder(tree); + + // Append all strings to builder. + List args = collectAll(tree); + for (JCTree t : args) { + gen.genExpr(t, t.type).load(); + appendString(t); + } + + // Convert builder to string. + builderToString(pos); + + return gen.getItems().makeStackItem(syms.stringType); + } + + private JCDiagnostic.DiagnosticPosition newStringBuilder(JCTree tree) { + JCDiagnostic.DiagnosticPosition pos = tree.pos(); + gen.getCode().emitop2(new_, gen.makeRef(pos, syms.stringBuilderType)); + gen.getCode().emitop0(dup); + gen.callMethod(pos, syms.stringBuilderType, names.init, List.nil(), false); + return pos; + } + + private void appendString(JCTree tree) { + Type t = tree.type.baseType(); + if (!t.isPrimitive() && t.tsym != syms.stringType.tsym) { + t = syms.objectType; + } + + Assert.checkNull(t.constValue()); + Symbol method = sbAppends.get(t); + if (method == null) { + method = rs.resolveInternalMethod(tree.pos(), gen.getAttrEnv(), syms.stringBuilderType, names.append, List.of(t), null); + sbAppends.put(t, method); + } + + gen.getItems().makeMemberItem(method, false).invoke(); + } + + private void builderToString(JCDiagnostic.DiagnosticPosition pos) { + gen.callMethod(pos, syms.stringBuilderType, names.toString, List.nil(), false); + } + } + + /** + * Base class for indified concatenation bytecode flavors. + */ + private static abstract class Indy extends StringConcat { + public Indy(Context context) { + super(context); + } + + @Override + public Item makeConcat(JCTree.JCAssignOp tree) { + List args = collectAll(tree.lhs, tree.rhs); + Item l = gen.genExpr(tree.lhs, tree.lhs.type); + emit(args, tree.type, tree.pos()); + return l; + } + + @Override + public Item makeConcat(JCTree.JCBinary tree) { + List args = collectAll(tree.lhs, tree.rhs); + emit(args, tree.type, tree.pos()); + return gen.getItems().makeStackItem(syms.stringType); + } + + protected abstract void emit(List args, Type type, JCDiagnostic.DiagnosticPosition pos); + + /** Peel the argument list into smaller chunks. */ + protected List> split(List args) { + ListBuffer> splits = new ListBuffer<>(); + + int slots = 0; + + // Need to peel, so that neither call has more than acceptable number + // of slots for the arguments. + ListBuffer cArgs = new ListBuffer<>(); + for (JCTree t : args) { + int needSlots = (t.type.getTag() == LONG || t.type.getTag() == DOUBLE) ? 2 : 1; + if (slots + needSlots >= MAX_INDY_CONCAT_ARG_SLOTS) { + splits.add(cArgs.toList()); + cArgs.clear(); + slots = 0; + } + cArgs.add(t); + slots += needSlots; + } + + // Flush the tail slice + if (!cArgs.isEmpty()) { + splits.add(cArgs.toList()); + } + + return splits.toList(); + } + } + + /** + * Emits the invokedynamic call to JDK java.lang.invoke.StringConcatFactory, + * without handling constants specially. + * + * We bypass empty strings, because they have no meaning at this level. This + * captures the Java language trick to force String concat with e.g. ("" + int)-like + * expression. Down here, we already know we are in String concat business, and do + * not require these markers. + */ + private static class IndyPlain extends Indy { + public IndyPlain(Context context) { + super(context); + } + + /** Emit the indy concat for all these arguments, possibly peeling along the way */ + protected void emit(List args, Type type, JCDiagnostic.DiagnosticPosition pos) { + List> split = split(args); + + for (List t : split) { + Assert.check(!t.isEmpty(), "Arguments list is empty"); + + ListBuffer dynamicArgs = new ListBuffer<>(); + for (JCTree arg : t) { + Object constVal = arg.type.constValue(); + if ("".equals(constVal)) continue; + if (arg.type == syms.botType) { + dynamicArgs.add(types.boxedClass(syms.voidType).type); + } else { + dynamicArgs.add(arg.type); + } + gen.genExpr(arg, arg.type).load(); + } + + doCall(type, pos, dynamicArgs.toList()); + } + + // More that one peel slice produced: concatenate the results + if (split.size() > 1) { + ListBuffer argTypes = new ListBuffer<>(); + for (int c = 0; c < split.size(); c++) { + argTypes.append(syms.stringType); + } + doCall(type, pos, argTypes.toList()); + } + } + + /** Produce the actual invokedynamic call to StringConcatFactory */ + private void doCall(Type type, JCDiagnostic.DiagnosticPosition pos, List dynamicArgTypes) { + Type.MethodType indyType = new Type.MethodType(dynamicArgTypes, + type, + List.nil(), + syms.methodClass); + + int prevPos = make.pos; + try { + make.at(pos); + + List bsm_staticArgs = List.of(syms.methodHandleLookupType, + syms.stringType, + syms.methodTypeType); + + Symbol bsm = rs.resolveInternalMethod(pos, + gen.getAttrEnv(), + syms.stringConcatFactory, + names.makeConcat, + bsm_staticArgs, + null); + + Symbol.DynamicMethodSymbol dynSym = new Symbol.DynamicMethodSymbol(names.makeConcat, + syms.noSymbol, + ClassFile.REF_invokeStatic, + (Symbol.MethodSymbol)bsm, + indyType, + List.nil().toArray()); + + Items.Item item = gen.getItems().makeDynamicItem(dynSym); + item.invoke(); + } finally { + make.at(prevPos); + } + } + } + + /** + * Emits the invokedynamic call to JDK java.lang.invoke.StringConcatFactory. + * This code concatenates all known constants into the recipe, possibly escaping + * some constants separately. + * + * We also bypass empty strings, because they have no meaning at this level. This + * captures the Java language trick to force String concat with e.g. ("" + int)-like + * expression. Down here, we already know we are in String concat business, and do + * not require these markers. + */ + private static final class IndyConstants extends Indy { + public IndyConstants(Context context) { + super(context); + } + + @Override + protected void emit(List args, Type type, JCDiagnostic.DiagnosticPosition pos) { + List> split = split(args); + + for (List t : split) { + Assert.check(!t.isEmpty(), "Arguments list is empty"); + + StringBuilder recipe = new StringBuilder(t.size()); + ListBuffer dynamicArgs = new ListBuffer<>(); + ListBuffer staticArgs = new ListBuffer<>(); + + for (JCTree arg : t) { + Object constVal = arg.type.constValue(); + if ("".equals(constVal)) continue; + if (arg.type == syms.botType) { + // Concat the null into the recipe right away + recipe.append((String) null); + } else if (constVal != null) { + // Concat the String representation of the constant, except + // for the case it contains special tags, which requires us + // to expose it as detached constant. + String a = arg.type.stringValue(); + if (a.indexOf(TAG_CONST) != -1 || a.indexOf(TAG_ARG) != -1) { + recipe.append(TAG_CONST); + staticArgs.add(a); + } else { + recipe.append(a); + } + } else { + // Ordinary arguments come through the dynamic arguments. + recipe.append(TAG_ARG); + dynamicArgs.add(arg.type); + gen.genExpr(arg, arg.type).load(); + } + } + + doCall(type, pos, recipe.toString(), staticArgs.toList(), dynamicArgs.toList()); + } + + // More that one peel slice produced: concatenate the results + // All arguments are assumed to be non-constant Strings. + if (split.size() > 1) { + ListBuffer argTypes = new ListBuffer<>(); + StringBuilder recipe = new StringBuilder(); + for (int c = 0; c < split.size(); c++) { + argTypes.append(syms.stringType); + recipe.append(TAG_ARG); + } + doCall(type, pos, recipe.toString(), List.nil(), argTypes.toList()); + } + } + + /** Produce the actual invokedynamic call to StringConcatFactory */ + private void doCall(Type type, JCDiagnostic.DiagnosticPosition pos, String recipe, List staticArgs, List dynamicArgTypes) { + Type.MethodType indyType = new Type.MethodType(dynamicArgTypes, + type, + List.nil(), + syms.methodClass); + + int prevPos = make.pos; + try { + make.at(pos); + + ListBuffer constTypes = new ListBuffer<>(); + ListBuffer constants = new ListBuffer<>(); + for (Object t : staticArgs) { + constants.add(t); + constTypes.add(syms.stringType); + } + + List bsm_staticArgs = List.of(syms.methodHandleLookupType, + syms.stringType, + syms.methodTypeType) + .append(syms.stringType) + .appendList(constTypes); + + Symbol bsm = rs.resolveInternalMethod(pos, + gen.getAttrEnv(), + syms.stringConcatFactory, + names.makeConcatWithConstants, + bsm_staticArgs, + null); + + Symbol.DynamicMethodSymbol dynSym = new Symbol.DynamicMethodSymbol(names.makeConcatWithConstants, + syms.noSymbol, + ClassFile.REF_invokeStatic, + (Symbol.MethodSymbol)bsm, + indyType, + List.of(recipe).appendList(constants).toArray()); + + Items.Item item = gen.getItems().makeDynamicItem(dynSym); + item.invoke(); + } finally { + make.at(prevPos); + } + } + } + +} diff -r 36c7b4ec7a8b -r 96661d1df628 langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java Thu Jan 28 14:06:27 2016 +0000 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java Thu Jan 28 19:42:46 2016 +0300 @@ -135,4 +135,10 @@ return hasInvokedynamic(); } + /** Does the target JDK contain StringConcatFactory class? + */ + public boolean hasStringConcatFactory() { + return compareTo(JDK1_9) >= 0; + } + } diff -r 36c7b4ec7a8b -r 96661d1df628 langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java Thu Jan 28 14:06:27 2016 +0000 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java Thu Jan 28 19:42:46 2016 +0300 @@ -179,6 +179,10 @@ public final Name altMetafactory; public final Name dollarThis; + // string concat + public final Name makeConcat; + public final Name makeConcatWithConstants; + public final Name.Table table; public Names(Context context) { @@ -316,6 +320,10 @@ lambda = fromString("lambda$"); metafactory = fromString("metafactory"); altMetafactory = fromString("altMetafactory"); + + // string concat + makeConcat = fromString("makeConcat"); + makeConcatWithConstants = fromString("makeConcatWithConstants"); } protected Name.Table createTable(Options options) { diff -r 36c7b4ec7a8b -r 96661d1df628 langtools/test/tools/javac/T5024091/T5024091.java --- a/langtools/test/tools/javac/T5024091/T5024091.java Thu Jan 28 14:06:27 2016 +0000 +++ b/langtools/test/tools/javac/T5024091/T5024091.java Thu Jan 28 19:42:46 2016 +0300 @@ -3,7 +3,7 @@ * @bug 5024091 * @summary AssertionError shouldn't be thrown * @author Wei Tao - * @compile/fail/ref=T5024091.out -XDfailcomplete=java.lang.StringBuilder -XDdev -XDrawDiagnostics T5024091.java + * @compile/fail/ref=T5024091.out -XDfailcomplete=java.lang.StringBuilder -XDdev -XDrawDiagnostics -XDstringConcat=inline T5024091.java */ public class T5024091 { diff -r 36c7b4ec7a8b -r 96661d1df628 langtools/test/tools/javac/TestIndyStringConcat.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/TestIndyStringConcat.java Thu Jan 28 19:42:46 2016 +0300 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test that StringConcat is working for JDK >= 9 + * @compile -source 6 -target 6 TestIndyStringConcat.java + * @run main TestIndyStringConcat false + * @clean TestIndyStringConcat* + * @compile -source 7 -target 7 TestIndyStringConcat.java + * @run main TestIndyStringConcat false + * @clean TestIndyStringConcat* + * @compile -source 8 -target 8 TestIndyStringConcat.java + * @run main TestIndyStringConcat false + * @clean TestIndyStringConcat* + * @compile -XDstringConcat=inline -source 9 -target 9 TestIndyStringConcat.java + * @run main TestIndyStringConcat false + * @clean TestIndyStringConcat* + * @compile -XDstringConcat=indy -source 9 -target 9 TestIndyStringConcat.java + * @run main TestIndyStringConcat true + * @clean TestIndyStringConcat* + * @compile -XDstringConcat=indyWithConstants -source 9 -target 9 TestIndyStringConcat.java + * @run main TestIndyStringConcat true + */ +public class TestIndyStringConcat { + + private static class MyObject { + public String toString() { + throw new RuntimeException("Boyyaa"); + } + } + + class Inner { } + + public static void main(String[] args) { + boolean useIndyConcat = Boolean.valueOf(args[0]); + try { + String s = "Foo" + new MyObject(); + } catch (RuntimeException ex) { + boolean indifiedStringConcat = false; + ex.printStackTrace(); + for (StackTraceElement e : ex.getStackTrace()) { + if (e.getClassName().startsWith("java.lang.String$Concat") && + e.getMethodName().equals("concat")) { + indifiedStringConcat = true; + break; + } + } + if (indifiedStringConcat != useIndyConcat) { + throw new AssertionError(); + } + } + } +} diff -r 36c7b4ec7a8b -r 96661d1df628 langtools/test/tools/javap/T6868539.java --- a/langtools/test/tools/javap/T6868539.java Thu Jan 28 14:06:27 2016 +0000 +++ b/langtools/test/tools/javap/T6868539.java Thu Jan 28 19:42:46 2016 +0300 @@ -58,10 +58,12 @@ throw new Error(errors + " found."); } + String notFound = " not found"; + void verify(String output, String... expects) { for (String expect: expects) { if (!output.matches("(?s).*" + expect + ".*")) - error(expect + " not found"); + error(expect + notFound); } }