8148483: JEP 280: Indify String Concatenation

Co-authored-by: Remi Forax <forax@univ-mlv.fr>
Co-authored-by: Peter Levart <peter.levart@gmail.com>
Reviewed-by: psandoz, mcimadamore, igerasim, forax, plevart, vlivanov, ihse
This commit is contained in:
Aleksey Shipilev 2016-01-28 19:42:46 +03:00
parent 8aa9bf5cc6
commit fca18168e4
8 changed files with 597 additions and 98 deletions

View File

@ -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.ClassSymbol;
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.JCPrimitiveType;
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 com.sun.tools.javac.util.Names;
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 class Symtab {
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 @@ public class Symtab {
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 @@ public class Symtab {
synthesizeEmptyInterfaceIfMissing(serializableType);
synthesizeEmptyInterfaceIfMissing(lambdaMetafactory);
synthesizeEmptyInterfaceIfMissing(serializedLambdaType);
synthesizeEmptyInterfaceIfMissing(stringConcatFactory);
synthesizeBoxTypeIfMissing(doubleType);
synthesizeBoxTypeIfMissing(floatType);
synthesizeBoxTypeIfMissing(voidType);

View File

@ -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 com.sun.tools.javac.tree.JCTree.*;
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 @@ public class Gen extends JCTree.Visitor {
private final TreeMaker make;
private final Names names;
private final Target target;
private final Map<Type,Symbol> 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 @@ public class Gen extends JCTree.Visitor {
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 class Gen extends JCTree.Visitor {
}
}
public Code getCode() {
return code;
}
public Items getItems() {
return items;
}
public Env<AttrContext> 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 @@ public class Gen extends JCTree.Visitor {
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 class Gen extends JCTree.Visitor {
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 @@ public class Gen extends JCTree.Visitor {
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.<Type>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.<Type>nil(),
false);
}
/** Complete generating code for operation, with left operand
* already on stack.

View File

@ -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.
*
* <p><b>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.</b>
*/
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<Type, Symbol> sbAppends;
protected final Resolve rs;
protected static final Context.Key<StringConcat> 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<JCTree> collectAll(JCTree tree) {
return collect(tree, List.nil());
}
protected List<JCTree> collectAll(JCTree.JCExpression lhs, JCTree.JCExpression rhs) {
return List.<JCTree>nil()
.appendList(collectAll(lhs))
.appendList(collectAll(rhs));
}
private List<JCTree> collect(JCTree tree, List<JCTree> 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<JCTree> 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<JCTree> 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.<Type>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.<Type>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<JCTree> 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<JCTree> args = collectAll(tree.lhs, tree.rhs);
emit(args, tree.type, tree.pos());
return gen.getItems().makeStackItem(syms.stringType);
}
protected abstract void emit(List<JCTree> args, Type type, JCDiagnostic.DiagnosticPosition pos);
/** Peel the argument list into smaller chunks. */
protected List<List<JCTree>> split(List<JCTree> args) {
ListBuffer<List<JCTree>> splits = new ListBuffer<>();
int slots = 0;
// Need to peel, so that neither call has more than acceptable number
// of slots for the arguments.
ListBuffer<JCTree> 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<JCTree> args, Type type, JCDiagnostic.DiagnosticPosition pos) {
List<List<JCTree>> split = split(args);
for (List<JCTree> t : split) {
Assert.check(!t.isEmpty(), "Arguments list is empty");
ListBuffer<Type> 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<Type> 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<Type> dynamicArgTypes) {
Type.MethodType indyType = new Type.MethodType(dynamicArgTypes,
type,
List.<Type>nil(),
syms.methodClass);
int prevPos = make.pos;
try {
make.at(pos);
List<Type> 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<JCTree> args, Type type, JCDiagnostic.DiagnosticPosition pos) {
List<List<JCTree>> split = split(args);
for (List<JCTree> t : split) {
Assert.check(!t.isEmpty(), "Arguments list is empty");
StringBuilder recipe = new StringBuilder(t.size());
ListBuffer<Type> dynamicArgs = new ListBuffer<>();
ListBuffer<Object> 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<Type> 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<Object> staticArgs, List<Type> dynamicArgTypes) {
Type.MethodType indyType = new Type.MethodType(dynamicArgTypes,
type,
List.<Type>nil(),
syms.methodClass);
int prevPos = make.pos;
try {
make.at(pos);
ListBuffer<Type> constTypes = new ListBuffer<>();
ListBuffer<Object> constants = new ListBuffer<>();
for (Object t : staticArgs) {
constants.add(t);
constTypes.add(syms.stringType);
}
List<Type> 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.<Object>of(recipe).appendList(constants).toArray());
Items.Item item = gen.getItems().makeDynamicItem(dynSym);
item.invoke();
} finally {
make.at(prevPos);
}
}
}
}

View File

@ -135,4 +135,10 @@ public enum Target {
return hasInvokedynamic();
}
/** Does the target JDK contain StringConcatFactory class?
*/
public boolean hasStringConcatFactory() {
return compareTo(JDK1_9) >= 0;
}
}

View File

@ -179,6 +179,10 @@ public class Names {
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 @@ public class Names {
lambda = fromString("lambda$");
metafactory = fromString("metafactory");
altMetafactory = fromString("altMetafactory");
// string concat
makeConcat = fromString("makeConcat");
makeConcatWithConstants = fromString("makeConcatWithConstants");
}
protected Name.Table createTable(Options options) {

View File

@ -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 {

View File

@ -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();
}
}
}
}

View File

@ -58,10 +58,12 @@ public class T6868539
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);
}
}