All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.jdk.compiler.share.classes.com.sun.tools.javac.jvm.StringConcat Maven / Gradle / Ivy

Go to download

"nb-javac" is a patched version of OpenJDK "javac", i.e., the Java compiler. This has long been part of NetBeans, providing a highly tuned Java compiler specifically for the Java editor i.e., parsing and lexing for features such as syntax coloring, code completion.

There is a newer version: 17.0.0.0
Show newest version
/*
 * Copyright (c) 2015, 2021, 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.code.Symbol.MethodSymbol;
import com.sun.tools.javac.comp.Resolve;
import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant;
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.*;
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 && 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), 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 abstract static 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); l.duplicate(); l.load(); emit(tree.pos(), args, false, tree.type); return l; } @Override public Item makeConcat(JCTree.JCBinary tree) { List args = collectAll(tree.lhs, tree.rhs); emit(tree.pos(), args, true, tree.type); return gen.getItems().makeStackItem(syms.stringType); } protected abstract void emit(JCDiagnostic.DiagnosticPosition pos, List args, boolean generateFirstArg, Type type); /** 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(); } /** * Returns true if the argument should be converted to a string eagerly, to preserve * possible side-effects. */ protected boolean shouldConvertToStringEagerly(Type argType) { return !types.unboxedTypeOrType(argType).isPrimitive() && argType.tsym != syms.stringType.tsym; } } /** * 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(JCDiagnostic.DiagnosticPosition pos, List args, boolean generateFirstArg, Type type) { List> split = split(args); boolean first = true; 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; Type argType = arg.type; if (argType == syms.botType) { argType = types.boxedClass(syms.voidType).type; } if (!first || generateFirstArg) { gen.genExpr(arg, arg.type).load(); } if (shouldConvertToStringEagerly(argType)) { gen.callMethod(pos, syms.stringType, names.valueOf, List.of(syms.objectType), true); argType = syms.stringType; } dynamicArgs.add(argType); first = false; } 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); MethodSymbol bsm = rs.resolveInternalMethod(pos, gen.getAttrEnv(), syms.stringConcatFactory, names.makeConcat, bsm_staticArgs, null); Symbol.DynamicMethodSymbol dynSym = new Symbol.DynamicMethodSymbol(names.makeConcat, syms.noSymbol, bsm.asHandle(), indyType, List.nil().toArray(new LoadableConstant[0])); 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(JCDiagnostic.DiagnosticPosition pos, List args, boolean generateFirstArg, Type type) { List> split = split(args); boolean first = true; 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(LoadableConstant.String(a)); } else { recipe.append(a); } } else { // Ordinary arguments come through the dynamic arguments. recipe.append(TAG_ARG); Type argType = arg.type; if (!first || generateFirstArg) { gen.genExpr(arg, arg.type).load(); } if (shouldConvertToStringEagerly(argType)) { gen.callMethod(pos, syms.stringType, names.valueOf, List.of(syms.objectType), true); argType = syms.stringType; } dynamicArgs.add(argType); first = false; } } 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 (LoadableConstant 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); MethodSymbol bsm = rs.resolveInternalMethod(pos, gen.getAttrEnv(), syms.stringConcatFactory, names.makeConcatWithConstants, bsm_staticArgs, null); Symbol.DynamicMethodSymbol dynSym = new Symbol.DynamicMethodSymbol(names.makeConcatWithConstants, syms.noSymbol, bsm.asHandle(), indyType, List.of(LoadableConstant.String(recipe)) .appendList(constants).toArray(new LoadableConstant[constants.size()])); Items.Item item = gen.getItems().makeDynamicItem(dynSym); item.invoke(); } finally { make.at(prevPos); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy