Skip to content

Commit

Permalink
[23] JEP 455: Primitive Types in Patterns, instanceof, and switch
Browse files Browse the repository at this point in the history
(Preview)

- removed unused arg typeId from invokeDynamic() (several)

+ new switchBit Primitive identifies switch over primitive
+ switching over all int-ish primitives
  - not yet: long, float, double
+ route primitive constants through indy typeSwitch with 2 args:
  - the constant (int or boolean)
  - invocation of ConstantBootstraps.primitiveClass
+ for boolean constants specifically route the constant through
  invocation of ConstantBootstraps.getStaticFinal ( TRUE | FALSE )
+ uniquely generate new bootstraps controlled by
  ClassFile.PRIMITIVE_CLASS__TOKEN and ..GET_STATIC_FINAL__TOKEN
+ no requireNonNull() for primitives

+ generic test for most primitives with identity conversion
  + using a constant case & a pattern case

fixes eclipse-jdt#2298
  • Loading branch information
stephan-herrmann committed Aug 27, 2024
1 parent 498ccb4 commit 83a3cac
Show file tree
Hide file tree
Showing 12 changed files with 1,490 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
import org.eclipse.jdt.internal.compiler.codegen.StackMapFrameCodeStream.ExceptionMarker;
import org.eclipse.jdt.internal.compiler.codegen.TypeAnnotationCodeStream;
import org.eclipse.jdt.internal.compiler.codegen.VerificationTypeInfo;
import org.eclipse.jdt.internal.compiler.impl.BooleanConstant;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.StringConstant;
Expand Down Expand Up @@ -205,8 +206,14 @@ public class ClassFile implements TypeConstants, TypeIds {
public static final String ENUMDESC_OF = "EnumDesc.of"; //$NON-NLS-1$
public static final String CLASSDESC = "ClassDesc"; //$NON-NLS-1$
public static final String CLASSDESC_OF = "ClassDesc.of"; //$NON-NLS-1$
public static final String CONSTANT_BOOTSTRAP__GET_STATIC_FINAL = "ConstantBootStraps.getStaticFinal"; //$NON-NLS-1$
public static final String CONSTANT_BOOTSTRAP__PRIMITIVE_CLASS = "ConstantBootStraps.primitiveClass"; //$NON-NLS-1$
public static final String[] BOOTSTRAP_METHODS = { ALTMETAFACTORY_STRING, METAFACTORY_STRING, BOOTSTRAP_STRING,
TYPESWITCH_STRING, ENUMSWITCH_STRING, CONCAT_CONSTANTS, INVOKE_STRING, ENUMDESC_OF, CLASSDESC, CLASSDESC_OF};
TYPESWITCH_STRING, ENUMSWITCH_STRING, CONCAT_CONSTANTS, INVOKE_STRING, ENUMDESC_OF, CLASSDESC, CLASSDESC_OF,
CONSTANT_BOOTSTRAP__GET_STATIC_FINAL, CONSTANT_BOOTSTRAP__PRIMITIVE_CLASS };

public static final Object PRIMITIVE_CLASS__TOKEN = new Object();
public static final Object GET_STATIC_FINAL__TOKEN = new Object();
/**
* INTERNAL USE-ONLY
* Request the creation of a ClassFile compatible representation of a problematic type
Expand Down Expand Up @@ -3675,6 +3682,10 @@ private int generateBootstrapMethods(List<Object> bootStrapMethodsList) {
localContentsOffset = addBootStrapTypeCaseConstantEntry(localContentsOffset, (ResolvedCase) o, fPtr);
} else if (o instanceof TypeBinding) {
localContentsOffset = addClassDescBootstrap(localContentsOffset, (TypeBinding) o, fPtr);
} else if (o == PRIMITIVE_CLASS__TOKEN) {
localContentsOffset = addPrimitiveClassBootstrap(localContentsOffset, fPtr);
} else if (o == GET_STATIC_FINAL__TOKEN) {
localContentsOffset = addGetBooleanConstantsBootstrap(localContentsOffset, fPtr);
}
}

Expand Down Expand Up @@ -3959,6 +3970,55 @@ private int addClassDescBootstrap(int localContentsOffset, TypeBinding type, Map

return localContentsOffset;
}
private int addPrimitiveClassBootstrap(int localContentsOffset, Map<String, Integer> fPtr) {
final int contentsEntries = 4;
int idx = fPtr.get(CONSTANT_BOOTSTRAP__PRIMITIVE_CLASS);
if (contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(contentsEntries);
}
if (idx == 0) {
idx = this.constantPool.literalIndexForMethodHandle(
ClassFileConstants.MethodHandleRefKindInvokeStatic,
this.referenceBinding.scope.getJavaLangInvokeConstantBootstraps(),
TypeConstants.PRIMITIVE_CLASS,
TypeConstants.PRIMITIVE_CLASS__SIGNATURE,
false);
fPtr.put(CONSTANT_BOOTSTRAP__PRIMITIVE_CLASS, idx);
}
this.contents[localContentsOffset++] = (byte) (idx >> 8);
this.contents[localContentsOffset++] = (byte) idx;

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

return localContentsOffset;
}
private int addGetBooleanConstantsBootstrap(int localContentsOffset, Map<String, Integer> fPtr) {
final int contentsEntries = 4;
int idx = fPtr.get(CONSTANT_BOOTSTRAP__GET_STATIC_FINAL);
if (contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(contentsEntries);
}
if (idx == 0) {
idx = this.constantPool.literalIndexForMethodHandle(
ClassFileConstants.MethodHandleRefKindInvokeStatic,
this.referenceBinding.scope.getJavaLangInvokeConstantBootstraps(),
TypeConstants.GET_STATIC_FINAL,
TypeConstants.GET_STATIC_FINAL__SIGNATURE,
false);
fPtr.put(CONSTANT_BOOTSTRAP__GET_STATIC_FINAL, idx);
}
this.contents[localContentsOffset++] = (byte) (idx >> 8);
this.contents[localContentsOffset++] = (byte) idx;

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

return localContentsOffset;
}

private int addBootStrapTypeSwitchEntry(int localContentsOffset, SwitchStatement switchStatement, Map<String, Integer> fPtr) {
CaseStatement.ResolvedCase[] constants = switchStatement.otherConstants;
int numArgs = constants.length;
Expand All @@ -3984,10 +4044,18 @@ private int addBootStrapTypeSwitchEntry(int localContentsOffset, SwitchStatement
localContentsOffset += 2;
for (CaseStatement.ResolvedCase c : constants) {
if (c.isPattern()) {
char[] typeName = c.t.constantPoolName();
int typeIndex = this.constantPool.literalIndexForType(typeName);
this.contents[localContentsOffset++] = (byte) (typeIndex >> 8);
this.contents[localContentsOffset++] = (byte) typeIndex;
int typeOrDynIndex;
if ((switchStatement.switchBits & SwitchStatement.Primitive) != 0) {
// Dynamic for Class.getPrimitiveClass(Z) or such
typeOrDynIndex = this.constantPool.literalIndexForDynamic(c.primitivesBootstrapIdx,
c.t.signature(),
ConstantPool.JavaLangClassSignature);
} else {
char[] typeName = c.t.constantPoolName();
typeOrDynIndex = this.constantPool.literalIndexForType(typeName);
}
this.contents[localContentsOffset++] = (byte) (typeOrDynIndex >> 8);
this.contents[localContentsOffset++] = (byte) typeOrDynIndex;
} else if (c.isQualifiedEnum()){
int typeIndex = this.constantPool.literalIndexForDynamic(c.enumDescIdx,
ConstantPool.INVOKE_METHOD_METHOD_NAME,
Expand All @@ -4001,10 +4069,21 @@ private int addBootStrapTypeSwitchEntry(int localContentsOffset, SwitchStatement
this.contents[localContentsOffset++] = (byte) intValIdx;
} else {
if (c.e instanceof NullLiteral) continue;
int intValIdx =
this.constantPool.literalIndex(c.intValue());
this.contents[localContentsOffset++] = (byte) (intValIdx >> 8);
this.contents[localContentsOffset++] = (byte) intValIdx;
int valIdx;
switch (c.t.id) {
case TypeIds.T_byte, TypeIds.T_char, TypeIds.T_short, TypeIds.T_int, TypeIds.T_long ->
valIdx = this.constantPool.literalIndex(c.intValue());
case TypeIds.T_boolean -> {
// Dynamic for Boolean.getStaticFinal(TRUE|FALSE)
valIdx = this.constantPool.literalIndexForDynamic(c.primitivesBootstrapIdx,
c.c.booleanValue() ? BooleanConstant.TRUE_STRING : BooleanConstant.FALSE_STRING,
ConstantPool.JavaLangBooleanSignature);
}
default ->
throw new RuntimeException("missing case impl"); // FIXME: handle long, float, double
}
this.contents[localContentsOffset++] = (byte) (valIdx >> 8);
this.contents[localContentsOffset++] = (byte) valIdx;
}
}

Expand Down Expand Up @@ -6398,6 +6477,27 @@ public int recordBootstrapMethod(TypeBinding type) {
this.bootstrapMethods.add(type);
return this.bootstrapMethods.size() - 1;
}
/**
* Record a singleton bootstrap method for the given token.
* @param token supported values:
* <dl>
* <dt>{@link #GET_STATIC_FINAL__TOKEN}<dd>handled by {@link #addPrimitiveClassBootstrap(int, Map)}
* <dt>{@link #PRIMITIVE_CLASS__TOKEN}<dd>handled by {@link #addGetBooleanConstantsBootstrap(int, Map)}
* </dl>
* @return the bootstrap index
*/
public int recordBootstrapMethodForPrimitiveHandling(Object token) {
if (this.bootstrapMethods == null) {
this.bootstrapMethods = new ArrayList<>();
} else {
int idx = this.bootstrapMethods.indexOf(token);
if (idx != -1) {
return idx;
}
}
this.bootstrapMethods.add(token);
return this.bootstrapMethods.size() - 1;
}
public int recordBootstrapMethod(String expression) {
if (this.bootstrapMethods == null) {
this.bootstrapMethods = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ public static class ResolvedCase {
private final boolean isQualifiedEnum;
public int enumDescIdx;
public int classDescIdx;
public int primitivesBootstrapIdx; // index for a bootstrap method to args to indy typeSwitch for primitives
ResolvedCase(Constant c, Expression e, TypeBinding t, int index, boolean isQualifiedEnum) {
this.c = c;
this.e = e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean
}
int invokeDynamicNumber = codeStream.classFile.recordBootstrapMethod(this);
codeStream.invokeDynamic(invokeDynamicNumber, (this.shouldCaptureInstance ? 1 : 0) + this.outerLocalVariablesSlotSize, 1, this.descriptor.selector, signature.toString().toCharArray(),
this.resolvedType.id, this.resolvedType);
this.resolvedType);
if (!valueRequired)
codeStream.pop();
codeStream.recordPositionsFrom(pc, this.sourceStart);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean
int invokeDynamicNumber = codeStream.classFile.recordBootstrapMethod(this);
codeStream.invokeDynamic(invokeDynamicNumber, argumentsSize, 1, this.descriptor.selector, buffer.toString().toCharArray(),
this.isConstructorReference(), (this.lhs instanceof TypeReference? (TypeReference) this.lhs : null), this.typeArguments,
this.resolvedType.id, this.resolvedType);
this.resolvedType);
if (!valueRequired)
codeStream.pop();
codeStream.recordPositionsFrom(pc, this.sourceStart);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
* This is an implementation of an early-draft specification developed under the Java
* Community Process (JCP) and is made available for testing and evaluation purposes
* only. The code is not compatible with any specification of the JCP.
*
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contributions for
Expand All @@ -24,7 +28,9 @@
import java.util.List;
import java.util.function.Function;
import java.util.function.IntPredicate;

import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ClassFile;
import org.eclipse.jdt.internal.compiler.ast.CaseStatement.ResolvedCase;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.BranchLabel;
Expand Down Expand Up @@ -88,6 +94,7 @@ public class SwitchStatement extends Expression {
public final static int TotalPattern = ASTNode.Bit3;
public final static int Exhaustive = ASTNode.Bit4;
public final static int QualifiedEnum = ASTNode.Bit5;
public final static int Primitive = ASTNode.Bit6;

// for switch on strings
private static final char[] SecretStringVariableName = " switchDispatchString".toCharArray(); //$NON-NLS-1$
Expand Down Expand Up @@ -954,7 +961,7 @@ private void generateCodeSwitchPatternEpilogue(CodeStream codeStream) {

private void generateCodeSwitchPatternPrologue(BlockScope currentScope, CodeStream codeStream) {
this.expression.generateCode(currentScope, codeStream, true);
if ((this.switchBits & NullCase) == 0) {
if ((this.switchBits & NullCase) == 0 && (this.switchBits & Primitive) == 0) {
codeStream.dup();
codeStream.invokeJavaUtilObjectsrequireNonNull();
codeStream.pop();
Expand Down Expand Up @@ -984,6 +991,18 @@ private void generateCodeSwitchPatternPrologue(BlockScope currentScope, CodeStre
if (hasQualifiedEnums) {
c.index = i;
}
if ((this.switchBits & Primitive) != 0) {
Object token = null;
if (c.isPattern()) {
token = ClassFile.PRIMITIVE_CLASS__TOKEN;
} else if (c.t.id == TypeIds.T_boolean) {
token = ClassFile.GET_STATIC_FINAL__TOKEN;
}
if (token != null) {
c.primitivesBootstrapIdx = codeStream.classFile.recordBootstrapMethodForPrimitiveHandling(token);
}
continue;
}
if (!c.isQualifiedEnum())
continue;
int classdescIdx = codeStream.classFile.recordBootstrapMethod(c.t);
Expand All @@ -993,12 +1012,14 @@ private void generateCodeSwitchPatternPrologue(BlockScope currentScope, CodeStre
}
}
private void generateTypeSwitchPatternPrologue(CodeStream codeStream, int invokeDynamicNumber) {
char[] signature = (this.switchBits & Primitive) != 0
? "(XI)I".replace("X", String.valueOf(this.expression.resolvedType.signature())).toCharArray() //$NON-NLS-1$ //$NON-NLS-2$
: "(Ljava/lang/Object;I)I".toCharArray(); //$NON-NLS-1$
codeStream.invokeDynamic(invokeDynamicNumber,
2, // Object, restartIndex
2, // Object / PRIM, restartIndex (PRIM = Z|S|I..)
1, // int
ConstantPool.TYPESWITCH,
"(Ljava/lang/Object;I)I".toCharArray(), //$NON-NLS-1$
TypeIds.T_int,
signature,
TypeBinding.INT);
}
private void generateEnumSwitchPatternPrologue(CodeStream codeStream, int invokeDynamicNumber) {
Expand All @@ -1009,7 +1030,6 @@ private void generateEnumSwitchPatternPrologue(CodeStream codeStream, int invoke
1, // int
"enumSwitch".toCharArray(), //$NON-NLS-1$
callingParams.toCharArray(),
TypeIds.T_int,
TypeBinding.INT);
}
protected void statementGenerateCode(BlockScope currentScope, CodeStream codeStream, Statement statement) {
Expand Down Expand Up @@ -1089,6 +1109,9 @@ public void resolve(BlockScope upperScope) {
expressionType = null; // fault-tolerance: ignore type mismatch from constants from hereon
break checkType;
} else if (expressionType.isBaseType()) {
if (JavaFeature.PRIMITIVES_IN_PATTERNS.isSupported(compilerOptions)) {
this.switchBits |= Primitive;
}
if (this.expression.isConstantValueOfTypeAssignableToType(expressionType, TypeBinding.INT))
break checkType;
if (expressionType.isCompatibleWith(TypeBinding.INT))
Expand All @@ -1112,8 +1135,10 @@ public void resolve(BlockScope upperScope) {
break checkType;
}
if (!JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(compilerOptions) || (expressionType.isBaseType() && expressionType.id != T_null && expressionType.id != T_void)) {
upperScope.problemReporter().incorrectSwitchType(this.expression, expressionType);
expressionType = null; // fault-tolerance: ignore type mismatch from constants from hereon
if ((this.switchBits & Primitive) == 0) { // when Primitive is set it is approved above
upperScope.problemReporter().incorrectSwitchType(this.expression, expressionType);
expressionType = null; // fault-tolerance: ignore type mismatch from constants from hereon
}
} else {
this.isNonTraditional = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2524,7 +2524,6 @@ public void invokeDynamicForStringConcat(StringBuilder recipe, List<TypeBinding>
1, // Ljava/lang/String;
ConstantPool.ConcatWithConstants,
signature.toString().toCharArray(),
TypeIds.T_JavaLangObject,
getPopularBinding(ConstantPool.JavaLangStringConstantPoolName));
}
/**
Expand Down Expand Up @@ -2951,7 +2950,7 @@ public void generateSyntheticBodyForDeserializeLambda(SyntheticMethodBinding met
}
// Example: invokeDynamic(0, 0, 1, "m".toCharArray(), "()Lcom/foo/X$Foo;".toCharArray());
invokeDynamic(funcEx.bootstrapMethodNumber, index, 1, funcEx.descriptor.selector,
sig.toString().toCharArray(), funcEx.resolvedType.id, funcEx.resolvedType);
sig.toString().toCharArray(), funcEx.resolvedType);
areturn();
if (j < count - 1) {
nextOne.place();
Expand Down Expand Up @@ -3385,23 +3384,23 @@ public void generateSyntheticBodyForRecordEquals(SyntheticMethodBinding methodBi
String sig = new String(methodBinding.signature());
sig = sig.substring(0, 1)+ new String(methodBinding.declaringClass.signature()) + sig.substring(1);
invokeDynamic(index, methodBinding.parameters.length, 1, methodBinding.selector, sig.toCharArray(),
TypeIds.T_boolean, TypeBinding.BOOLEAN);
TypeBinding.BOOLEAN);
ireturn();
}
public void generateSyntheticBodyForRecordHashCode(SyntheticMethodBinding methodBinding, int index) {
aload_0();
String sig = new String(methodBinding.signature());
sig = sig.substring(0, 1)+ new String(methodBinding.declaringClass.signature()) + sig.substring(1);
invokeDynamic(index, methodBinding.parameters.length, 1, methodBinding.selector, sig.toCharArray(),
TypeIds.T_int, TypeBinding.INT);
TypeBinding.INT);
ireturn();
}
public void generateSyntheticBodyForRecordToString(SyntheticMethodBinding methodBinding, int index) {
aload_0();
String sig = new String(methodBinding.signature());
sig = sig.substring(0, 1)+ new String(methodBinding.declaringClass.signature()) + sig.substring(1);
invokeDynamic(index, methodBinding.parameters.length, 1, methodBinding.selector, sig.toCharArray(),
TypeIds.T_JavaLangObject, getPopularBinding(ConstantPool.JavaLangStringConstantPoolName));
getPopularBinding(ConstantPool.JavaLangStringConstantPoolName));
areturn();
}

Expand Down Expand Up @@ -4549,12 +4548,12 @@ private void invoke18(byte opcode, int receiverAndArgsSize, int returnTypeSize,
}

public void invokeDynamic(int bootStrapIndex, int argsSize, int returnTypeSize, char[] selector, char[] signature,
int typeId, TypeBinding type) {
this.invokeDynamic(bootStrapIndex, argsSize, returnTypeSize, selector, signature, false, null, null, typeId, type);
TypeBinding type) {
this.invokeDynamic(bootStrapIndex, argsSize, returnTypeSize, selector, signature, false, null, null, type);
}

public void invokeDynamic(int bootStrapIndex, int argsSize, int returnTypeSize, char[] selector, char[] signature, boolean isConstructorReference, TypeReference lhsTypeReference, TypeReference [] typeArguments,
int typeId, TypeBinding type) {
TypeBinding type) {
if (this.classFileOffset + 4 >= this.bCodeStream.length) {
resizeByteArray();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ public class ConstantPool implements ClassFileConstants, TypeIds {
public static final char[] JavaIoPrintStreamSignature = "Ljava/io/PrintStream;".toCharArray(); //$NON-NLS-1$
public static final char[] JavaLangAssertionErrorConstantPoolName = "java/lang/AssertionError".toCharArray(); //$NON-NLS-1$
public static final char[] JavaLangBooleanConstantPoolName = "java/lang/Boolean".toCharArray(); //$NON-NLS-1$
public static final char[] JavaLangBooleanSignature = "Ljava/lang/Boolean;".toCharArray(); //$NON-NLS-1$
public static final char[] JavaLangByteConstantPoolName = "java/lang/Byte".toCharArray(); //$NON-NLS-1$
public static final char[] JavaLangCharacterConstantPoolName = "java/lang/Character".toCharArray(); //$NON-NLS-1$
public static final char[] JavaLangClassConstantPoolName = "java/lang/Class".toCharArray(); //$NON-NLS-1$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public void invoke(byte opcode, MethodBinding methodBinding, TypeBinding declari
@Override
public void invokeDynamic(int bootStrapIndex, int argsSize, int returnTypeSize, char[] selector, char[] signature,
boolean isConstructorReference, TypeReference lhsTypeReference, TypeReference [] typeArguments,
int typeId, TypeBinding type) {
TypeBinding type) {
if (lhsTypeReference != null && (lhsTypeReference.bits & ASTNode.HasTypeAnnotations) != 0) {
if (isConstructorReference) {
addAnnotationContext(lhsTypeReference, this.position, 0, AnnotationTargetTypeConstants.CONSTRUCTOR_REFERENCE);
Expand All @@ -155,7 +155,7 @@ public void invokeDynamic(int bootStrapIndex, int argsSize, int returnTypeSize,
}
}
}
super.invokeDynamic(bootStrapIndex, argsSize, returnTypeSize, selector, signature, isConstructorReference, lhsTypeReference, typeArguments, typeId, type);
super.invokeDynamic(bootStrapIndex, argsSize, returnTypeSize, selector, signature, isConstructorReference, lhsTypeReference, typeArguments, type);
}

@Override
Expand Down
Loading

0 comments on commit 83a3cac

Please sign in to comment.