Skip to content

Commit

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

+ implement code generation for switch on primitive/boxed
+ new error for illegal constant type in switch on newly allowed type
+ systematic test combinatorics of primitives in instanceof / switch

See #2866 for details.
  • Loading branch information
stephan-herrmann authored Aug 29, 2024
1 parent 498ccb4 commit 6b7f284
Show file tree
Hide file tree
Showing 18 changed files with 1,846 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2683,4 +2683,11 @@ public interface IProblem {
* @noreference preview feature
*/
int ConstructorCallNotAllowedHere = PreviewRelated + 2031;

/**
* @since 3.39
* @noreference preview feature
*/
int WrongCaseType = PreviewRelated + 2100;

}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
import org.eclipse.jdt.internal.compiler.ast.SwitchStatement.SingletonBootstrap;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
Expand All @@ -100,6 +101,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 +207,12 @@ 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 };

/**
* INTERNAL USE-ONLY
* Request the creation of a ClassFile compatible representation of a problematic type
Expand Down Expand Up @@ -3675,6 +3681,8 @@ 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 instanceof SingletonBootstrap sb) {
localContentsOffset = addSingletonBootstrap(localContentsOffset, sb, fPtr);
}
}

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

return localContentsOffset;
}

private int addSingletonBootstrap(int localContentsOffset, SingletonBootstrap sb, Map<String, Integer> fPtr) {
final int contentsEntries = 4;
int idx = fPtr.get(sb.id());
if (contentsEntries + localContentsOffset >= this.contents.length) {
resizeContents(contentsEntries);
}
if (idx == 0) {
idx = this.constantPool.literalIndexForMethodHandle(
ClassFileConstants.MethodHandleRefKindInvokeStatic,
this.referenceBinding.scope.getJavaLangInvokeConstantBootstraps(),
sb.selector(),
sb.signature(),
false);
fPtr.put(sb.id(), 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 +4018,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 +4043,24 @@ private int addBootStrapTypeSwitchEntry(int localContentsOffset, SwitchStatement
this.contents[localContentsOffset++] = (byte) intValIdx;
} else {
if (c.e instanceof NullLiteral) continue;
int intValIdx =
int valIdx = switch (c.t.id) {
case TypeIds.T_boolean -> // Dynamic for Boolean.getStaticFinal(TRUE|FALSE) :
this.constantPool.literalIndexForDynamic(c.primitivesBootstrapIdx,
c.c.booleanValue() ? BooleanConstant.TRUE_STRING : BooleanConstant.FALSE_STRING,
ConstantPool.JavaLangBooleanSignature);
case TypeIds.T_byte, TypeIds.T_char, TypeIds.T_short, TypeIds.T_int ->
this.constantPool.literalIndex(c.intValue());
this.contents[localContentsOffset++] = (byte) (intValIdx >> 8);
this.contents[localContentsOffset++] = (byte) intValIdx;
case TypeIds.T_long ->
this.constantPool.literalIndex(c.c.longValue());
case TypeIds.T_float ->
this.constantPool.literalIndex(c.c.floatValue());
case TypeIds.T_double ->
this.constantPool.literalIndex(c.c.doubleValue());
default ->
throw new IllegalArgumentException("Switch has unexpected type: "+switchStatement); //$NON-NLS-1$
};
this.contents[localContentsOffset++] = (byte) (valIdx >> 8);
this.contents[localContentsOffset++] = (byte) valIdx;
}
}

Expand Down Expand Up @@ -6398,6 +6454,23 @@ public int recordBootstrapMethod(TypeBinding type) {
this.bootstrapMethods.add(type);
return this.bootstrapMethods.size() - 1;
}
/**
* Record a singleton bootstrap method for the given token.
* @param descriptor represents the method to be bootstrapped
* @return the bootstrap index
*/
public int recordSingletonBootstrapMethod(SingletonBootstrap descriptor) {
if (this.bootstrapMethods == null) {
this.bootstrapMethods = new ArrayList<>();
} else {
int idx = this.bootstrapMethods.indexOf(descriptor);
if (idx != -1) {
return idx;
}
}
this.bootstrapMethods.add(descriptor);
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 Expand Up @@ -234,6 +235,25 @@ public ResolvedCase[] resolveCase(BlockScope scope, TypeBinding switchExpression
}
}
} else {
// check from §14.11.1 (JEP 455):
// For each case constant associated with the switch block that is a constant expression, one of the following is true:
// - [...]
// - if T is one of long, float, double, or boolean, the type of the case constant is T.
// - if T is one of Long, Float, Double, or Boolean, the type of the case constant is, respectively, long, float, double, or boolean.
TypeBinding expectedCaseType = switchExpressionType;
if (switchExpressionType.isBoxedPrimitiveType()) {
expectedCaseType = scope.environment().computeBoxingType(switchExpressionType); // in this case it's actually 'computeUnboxingType()'
}
switch (expectedCaseType.id) {
case TypeIds.T_long, TypeIds.T_float, TypeIds.T_double, TypeIds.T_boolean -> {
if (caseType.id != expectedCaseType.id) {
scope.problemReporter().caseExpressionWrongType(e, switchExpressionType, expectedCaseType);
continue;
}
switchExpressionType = expectedCaseType;
}
}
//
Constant con = resolveConstantExpression(scope, caseType, switchExpressionType, switchStatement, e, cases);
if (con != Constant.NotAConstant) {
int index = this == switchStatement.nullCase && e instanceof NullLiteral ?
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
Loading

0 comments on commit 6b7f284

Please sign in to comment.