Skip to content

Commit

Permalink
Bug 527899 [9] Implement JEP 280: Indify String Concatenation (eclips…
Browse files Browse the repository at this point in the history
…e-jdt#1139)

Alters the code generation for string concatenation expression to use the bootstrap method as specified by the JEP 280 for compliance 9 and above. A new compiler option org.eclipse.jdt.core.compiler.codegen.useStringConcatFactory has been introduced for this, which is enabled by default.
  • Loading branch information
jarthana authored Jun 29, 2023
1 parent cd2abc9 commit 7c1e979
Show file tree
Hide file tree
Showing 21 changed files with 1,682 additions and 590 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public class ClassFile implements TypeConstants, TypeIds {
public int headerOffset;
public Map<TypeBinding, Boolean> innerClassesBindings;
public Set<SourceTypeBinding> nestMembers;
public List<ASTNode> bootstrapMethods = null;
public List<Object> bootstrapMethods = null;
public int methodCount;
public int methodCountOffset;
// pool managment
Expand All @@ -194,7 +194,8 @@ public class ClassFile implements TypeConstants, TypeIds {
public static final String BOOTSTRAP_STRING = new String(ConstantPool.BOOTSTRAP);
public static final String TYPESWITCH_STRING = new String(ConstantPool.TYPESWITCH);
public static final String ENUMSWITCH_STRING = new String(ConstantPool.ENUMSWITCH);
public static final String[] BOOTSTRAP_METHODS = {ALTMETAFACTORY_STRING, METAFACTORY_STRING, BOOTSTRAP_STRING, TYPESWITCH_STRING, ENUMSWITCH_STRING};
public static final String CONCAT_CONSTANTS = new String(ConstantPool.ConcatWithConstants);
public static final String[] BOOTSTRAP_METHODS = {ALTMETAFACTORY_STRING, METAFACTORY_STRING, BOOTSTRAP_STRING, TYPESWITCH_STRING, ENUMSWITCH_STRING, CONCAT_CONSTANTS};

/**
* INTERNAL USE-ONLY
Expand Down Expand Up @@ -3602,7 +3603,7 @@ private Map<String, Integer> createInitBootStrapMethodsMap() {
}
return fPtr;
}
private int generateBootstrapMethods(List<ASTNode> bootStrapMethodsList) {
private int generateBootstrapMethods(List<Object> bootStrapMethodsList) {
/* See JVM spec 4.7.21
The BootstrapMethods attribute has the following format:
BootstrapMethods_attribute {
Expand Down Expand Up @@ -3657,6 +3658,8 @@ private int generateBootstrapMethods(List<ASTNode> bootStrapMethodsList) {
} else {
localContentsOffset = addBootStrapTypeSwitchEntry(localContentsOffset, stmt, fPtr);
}
} else if (o instanceof StringBuilder) {
localContentsOffset = addBootStrapStringConcatEntry(localContentsOffset, (StringBuilder) o, fPtr);
}
}

Expand Down Expand Up @@ -3930,7 +3933,32 @@ private int addBootStrapEnumSwitchEntry(int localContentsOffset, SwitchStatement

return localContentsOffset;
}
private int addBootStrapStringConcatEntry(int localContentsOffset, StringBuilder recipe, Map<String, Integer> fPtr) {
final int contentsEntries = 10;
int indexForStringConcat = fPtr.get(ClassFile.CONCAT_CONSTANTS);
if (contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(contentsEntries);
}
if (indexForStringConcat == 0) {
ReferenceBinding stringConcatBootstrap = this.referenceBinding.scope.getJavaLangInvokeStringConcatFactory();
indexForStringConcat = this.constantPool.literalIndexForMethodHandle(ClassFileConstants.MethodHandleRefKindInvokeStatic, stringConcatBootstrap,
ConstantPool.ConcatWithConstants, ConstantPool.JAVA_LANG_INVOKE_STRING_CONCAT_FACTORY_SIGNATURE, false);
fPtr.put(ClassFile.CONCAT_CONSTANTS, indexForStringConcat);
}
this.contents[localContentsOffset++] = (byte) (indexForStringConcat >> 8);
this.contents[localContentsOffset++] = (byte) indexForStringConcat;

// u2 num_bootstrap_arguments
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) 1;

int intValIdx =
this.constantPool.literalIndex(recipe.toString());
this.contents[localContentsOffset++] = (byte) (intValIdx >> 8);
this.contents[localContentsOffset++] = (byte) intValIdx;

return localContentsOffset;
}
private int generateLineNumberAttribute() {
int localContentsOffset = this.contentsOffset;
int attributesNumber = 0;
Expand Down Expand Up @@ -6212,7 +6240,7 @@ public int recordBootstrapMethod(FunctionalExpression expression) {
}
if (expression instanceof ReferenceExpression) {
for (int i = 0; i < this.bootstrapMethods.size(); i++) {
ASTNode node = this.bootstrapMethods.get(i);
Object node = this.bootstrapMethods.get(i);
if (node instanceof FunctionalExpression) {
FunctionalExpression fexp = (FunctionalExpression) node;
if (fexp.binding == expression.binding
Expand All @@ -6233,7 +6261,13 @@ public int recordBootstrapMethod(SwitchStatement switchStatement) {
this.bootstrapMethods.add(switchStatement);
return this.bootstrapMethods.size() - 1;
}

public int recordBootstrapMethod(StringBuilder expression) {
if (this.bootstrapMethods == null) {
this.bootstrapMethods = new ArrayList<>();
}
this.bootstrapMethods.add(expression);
return this.bootstrapMethods.size() - 1;
}
public void reset(/*@Nullable*/SourceTypeBinding typeBinding, CompilerOptions options) {
// the code stream is reinitialized for each method
if (typeBinding != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2020 IBM Corporation and others.
* Copyright (c) 2000, 2023 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -16,6 +16,8 @@
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import java.util.List;

import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
Expand Down Expand Up @@ -1562,7 +1564,20 @@ public void generateOptimizedLogicalXor(BlockScope currentScope, CodeStream code
}
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
}

@Override
public void buildStringForConcatation(BlockScope blockScope, CodeStream codeStream, int typeID, StringBuilder recipe, List<TypeBinding> argTypes) {
if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS)
&& ((this.bits & ASTNode.ReturnTypeIDMASK) == TypeIds.T_JavaLangString)) {
if (this.constant != Constant.NotAConstant) {
super.buildStringForConcatation(blockScope, codeStream, typeID, recipe, argTypes);
} else {
this.left.buildStringForConcatation(blockScope, codeStream, this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK, recipe, argTypes);
this.right.buildStringForConcatation(blockScope, codeStream, this.right.implicitConversion & TypeIds.COMPILE_TYPE_MASK, recipe, argTypes);
}
} else {
super.buildStringForConcatation(blockScope, codeStream, typeID, recipe, argTypes);
}
}
@Override
public void generateOptimizedStringConcatenation(BlockScope blockScope, CodeStream codeStream, int typeID) {
// keep implementation in sync with CombinedBinaryExpression
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2006, 2012 IBM Corporation and others.
* Copyright (c) 2006, 2023 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -15,6 +15,8 @@
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import java.util.List;

import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
Expand Down Expand Up @@ -236,6 +238,50 @@ public void generateOptimizedStringConcatenation(BlockScope blockScope,
}
}

@Override
public void buildStringForConcatation(BlockScope blockScope, CodeStream codeStream, int typeID, StringBuilder recipe, List<TypeBinding> argTypes) {
if (this.referencesTable == null) {
super.buildStringForConcatation(blockScope, codeStream, typeID, recipe, argTypes);
} else {
// copied from below method generateOptimizedStringConcatenationCreation(BlockScope, CodeStream, int)
if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) ==
OperatorIds.PLUS) &&
((this.bits & ASTNode.ReturnTypeIDMASK) ==
TypeIds.T_JavaLangString) &&
this.constant == Constant.NotAConstant) {
BinaryExpression cursor = this.referencesTable[this.arity - 1];
int restart = 0;
for (restart = this.arity - 1; restart >= 0; restart--) {
if (((((cursor = this.referencesTable[restart]).bits &
ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) ==
OperatorIds.PLUS) &&
((cursor.bits & ASTNode.ReturnTypeIDMASK) ==
TypeIds.T_JavaLangString)) {
if (cursor.constant != Constant.NotAConstant) {
super.buildStringForConcatation(blockScope, codeStream, typeID, recipe, argTypes);
break;
}
} else {
cursor.buildStringForConcatation(blockScope, codeStream, cursor.implicitConversion &
TypeIds.COMPILE_TYPE_MASK, recipe, argTypes);
break;
}
}
restart++;
if (restart == 0) { // reached the leftmost expression
cursor.left.buildStringForConcatation(blockScope, codeStream, cursor.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK, recipe, argTypes);
}
for (int i = restart; i < this.arity; i++) {
cursor = this.referencesTable[i];
cursor.right.buildStringForConcatation(blockScope, codeStream, cursor.right.implicitConversion &
TypeIds.COMPILE_TYPE_MASK, recipe, argTypes);
}
this.right.buildStringForConcatation(blockScope, codeStream, this.right.implicitConversion & TypeIds.COMPILE_TYPE_MASK, recipe, argTypes);
} else {
super.buildStringForConcatation(blockScope, codeStream, typeID, recipe, argTypes);
}
}
}
@Override
public void generateOptimizedStringConcatenationCreation(BlockScope blockScope,
CodeStream codeStream, int typeID) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2021 IBM Corporation and others.
* Copyright (c) 2000, 2023 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -35,6 +35,7 @@
package org.eclipse.jdt.internal.compiler.ast;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
Expand Down Expand Up @@ -68,7 +69,6 @@
public abstract class Expression extends Statement {

public Constant constant;

public int statementEnd = -1;

//Some expression may not be used - from a java semantic point
Expand Down Expand Up @@ -931,7 +931,45 @@ public void generateOptimizedStringConcatenationCreation(BlockScope blockScope,
}
codeStream.invokeStringConcatenationStringConstructor();
}

public void buildStringForConcatation(BlockScope blockScope, CodeStream codeStream, int typeID, StringBuilder recipe, List<TypeBinding> argTypes) {
if (this.constant == Constant.NotAConstant) {
switch (typeID) {
case T_JavaLangString :
case TypeIds.T_int :
case TypeIds.T_byte :
case TypeIds.T_short :
case TypeIds.T_long :
case TypeIds.T_float :
case TypeIds.T_double :
case TypeIds.T_char :
case TypeIds.T_boolean :
generateCode(blockScope, codeStream, true);
argTypes.add(this.resolvedType);
recipe.append(STRING_CONCAT_MARKER_1);
break;
default :
if (this.resolvedType.id == TypeIds.T_null) {
// Optimize it, avoid aconst_null, simply append the String Literal
recipe.append((String) null);
} else {
generateCode(blockScope, codeStream, true);
codeStream.invokeStringValueOf(typeID);
argTypes.add(blockScope.getJavaLangString());
recipe.append(STRING_CONCAT_MARKER_1);
}
break;
}
} else {
// StringLiteral and CharLiteral may contain special characters
if (this.constant.stringValue().indexOf('\u0001') != -1 || this.constant.stringValue().indexOf('\u0002') != -1) {
codeStream.ldc(this.constant.stringValue());
recipe.append(STRING_CONCAT_MARKER_1);
argTypes.add(blockScope.getJavaLangString());
} else {
recipe.append(this.constant.stringValue());
}
}
}
private MethodBinding[] getAllOriginalInheritedMethods(ReferenceBinding binding) {
ArrayList<MethodBinding> collector = new ArrayList<>();
getAllInheritedMethods0(binding, collector);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2615,26 +2615,58 @@ public void generateReturnBytecode(Expression expression) {
* @param oper2 the second expression
*/
public void generateStringConcatenationAppend(BlockScope blockScope, Expression oper1, Expression oper2) {
int pc;
if (oper1 == null) {
/* Operand is already on the stack, and maybe nil:
note type1 is always to java.lang.String here.*/
newStringContatenation();
dup_x1();
this.swap();
// If argument is reference type, need to transform it
// into a string (handles null case)
invokeStringValueOf(TypeIds.T_JavaLangObject);
invokeStringConcatenationStringConstructor();
if (this.targetLevel >= ClassFileConstants.JDK9 && blockScope.compilerOptions().useStringConcatFactory) {
this.countLabels = 0;
this.stackDepth++;
if (this.stackDepth > this.stackMax) {
this.stackMax = this.stackDepth;
}
StringBuilder recipe = new StringBuilder();
List<TypeBinding> arguments = new ArrayList<>();
if (oper1 == null) {
// Operand is already on the stack
invokeStringValueOf(TypeIds.T_JavaLangObject);
arguments.add(blockScope.getJavaLangString());
recipe.append(TypeConstants.STRING_CONCAT_MARKER_1);
} else {
oper1.buildStringForConcatation(blockScope, this, oper1.implicitConversion & TypeIds.COMPILE_TYPE_MASK, recipe, arguments);
}
oper2.buildStringForConcatation(blockScope, this, oper2.implicitConversion & TypeIds.COMPILE_TYPE_MASK, recipe, arguments);
int invokeDynamicNumber = this.classFile.recordBootstrapMethod(recipe);
StringBuilder signature = new StringBuilder("("); //$NON-NLS-1$
for(int i = 0; i < arguments.size(); i++) {
signature.append(arguments.get(i).signature());
}
signature.append(")Ljava/lang/String;"); //$NON-NLS-1$
this.invokeDynamic(invokeDynamicNumber,
2,
1, // int
ConstantPool.ConcatWithConstants,
signature.toString().toCharArray(),
TypeIds.T_JavaLangObject,
getPopularBinding(ConstantPool.JavaLangStringConstantPoolName));
} else {
int pc;
if (oper1 == null) {
/* Operand is already on the stack, and maybe nil:
note type1 is always to java.lang.String here.*/
newStringContatenation();
dup_x1();
this.swap();
// If argument is reference type, need to transform it
// into a string (handles null case)
invokeStringValueOf(TypeIds.T_JavaLangObject);
invokeStringConcatenationStringConstructor();
} else {
pc = this.position;
oper1.generateOptimizedStringConcatenationCreation(blockScope, this, oper1.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
this.recordPositionsFrom(pc, oper1.sourceStart);
}
pc = this.position;
oper1.generateOptimizedStringConcatenationCreation(blockScope, this, oper1.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
this.recordPositionsFrom(pc, oper1.sourceStart);
oper2.generateOptimizedStringConcatenation(blockScope, this, oper2.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
this.recordPositionsFrom(pc, oper2.sourceStart);
invokeStringConcatenationToString();
}
pc = this.position;
oper2.generateOptimizedStringConcatenation(blockScope, this, oper2.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
this.recordPositionsFrom(pc, oper2.sourceStart);
invokeStringConcatenationToString();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,9 @@ public class ConstantPool implements ClassFileConstants, TypeIds {
public static final char[] TYPESWITCH = "typeSwitch".toCharArray(); //$NON-NLS-1$
public static final char[] ENUMSWITCH = "enumSwitch".toCharArray(); //$NON-NLS-1$
public static final char[] JAVA_LANG_RUNTIME_SWITCHBOOTSTRAPS_SWITCH_SIGNATURE = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;".toCharArray(); //$NON-NLS-1$

public static final char[] ConcatWithConstants = "makeConcatWithConstants".toCharArray(); //$NON-NLS-1$
public static final char[] JAVA_LANG_INVOKE_STRING_CONCAT_FACTORY_SIGNATURE =
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;".toCharArray(); //$NON-NLS-1$
/**
* ConstantPool constructor comment.
*/
Expand Down
Loading

0 comments on commit 7c1e979

Please sign in to comment.