Skip to content

Commit

Permalink
[Patterns][records] Reimplement code generation for record patterns (#…
Browse files Browse the repository at this point in the history
…2000)

* Reimplement code generation for record patterns
* Fixes #300
  • Loading branch information
srikanth-sankaran authored Feb 12, 2024
1 parent e5ccbed commit 8004cb6
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 179 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6582,7 +6582,7 @@ public int compare(StackMapFrame frame, StackMapFrame frame2) {
}

private TypeBinding getTypeBinding(char[] typeConstantPoolName, Scope scope, boolean checkcast) {
if (typeConstantPoolName.length == 1) {
if (typeConstantPoolName.length == 1 && !checkcast) {
// base type
switch(typeConstantPoolName[0]) {
case 'Z':
Expand Down Expand Up @@ -6880,6 +6880,7 @@ public List<StackMapFrame> traverse(
}
}
byte opcode = (byte) u1At(bytecodes, 0, pc);
inspectFrame(currentPC, frame);
switch (opcode) {
case Opcodes.OPC_nop:
pc++;
Expand Down Expand Up @@ -7863,6 +7864,10 @@ public List<StackMapFrame> traverse(
return filterFakeFrames(realJumpTarget, frames, codeLength);
}

private void inspectFrame(int currentPC, StackMapFrame frame) {
// Plant a breakpoint at the call site to conveniently hover.
}

private StackMapFrame createNewFrame(int currentPC, StackMapFrame frame, boolean isClinit, MethodBinding methodBinding) {
StackMapFrame newFrame = frame.duplicate();
newFrame.pc = currentPC;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,4 @@ public void resumeVariables(CodeStream codeStream, BlockScope scope) {
protected boolean isPatternTypeCompatible(TypeBinding other, BlockScope scope) {
return this.primaryPattern.isPatternTypeCompatible(other, scope);
}
@Override
public void wrapupGeneration(CodeStream codeStream) {
this.primaryPattern.wrapupGeneration(codeStream);
}
@Override
protected void generatePatternVariable(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel,
BranchLabel falseLabel) {
this.primaryPattern.generatePatternVariable(currentScope, codeStream, trueLabel, falseLabel);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStr
codeStream.ifeq(nextSibling);
if (this.secretExpressionValue != null) {
codeStream.load(this.secretExpressionValue);
codeStream.removeVariable(this.secretExpressionValue);
} else {
this.expression.generateCode(currentScope, codeStream, true);
}
Expand All @@ -175,10 +176,6 @@ public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStr
codeStream.store(this.elementVariable.binding, false);
}

if (this.secretExpressionValue != null) {
codeStream.removeVariable(this.secretExpressionValue);
}

if (valueRequired) {
codeStream.generateImplicitConversion(this.implicitConversion);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,19 @@
import org.eclipse.jdt.internal.compiler.codegen.BranchLabel;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;

public abstract class Pattern extends Expression {

/* package */ boolean isTotalTypeNode = false;
static final String SECRET_PATTERN_VARIABLE_NAME = " secretPatternVariable"; //$NON-NLS-1$

public LocalVariableBinding secretPatternVariable = null;

private Pattern enclosingPattern;
protected MethodBinding accessorMethod;
/* package */ BranchLabel elseTarget;
/* package */ BranchLabel thenTarget;

public int nestingLevel = 0;

// denotes index of this pattern in the parent record pattern, or -1 for patterns whose parent is not a record pattern
public int index = -1;
public int index = -1; // index of this in enclosing record pattern, or -1 for top level patterns

@Override
public boolean containsPatternVariable() {
Expand Down Expand Up @@ -67,9 +60,8 @@ public Pattern getEnclosingPattern() {
/**
* @param enclosingPattern the enclosingPattern to set
*/
public void setEnclosingPattern(Pattern enclosingPattern) {
public void setEnclosingPattern(RecordPattern enclosingPattern) {
this.enclosingPattern = enclosingPattern;
this.nestingLevel = enclosingPattern.nestingLevel+1;
}
/**
* Implement the rules in the spec under 14.11.1.1 Exhaustive Switch Blocks
Expand Down Expand Up @@ -100,8 +92,6 @@ public void resumeVariables(CodeStream codeStream, BlockScope scope) {
// nothing by default
}
public abstract void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel);
protected abstract void generatePatternVariable(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel);
protected abstract void wrapupGeneration(CodeStream codeStream);

public TypeReference getType() {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
package org.eclipse.jdt.internal.compiler.ast;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.codegen.BranchLabel;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
Expand All @@ -35,8 +33,6 @@

public class RecordPattern extends TypePattern {

public static final String SECRET_RECORD_PATTERN_THROWABLE_VARIABLE_NAME = " secretRecordPatternThrowableVariable"; //$NON-NLS-1$;

public Pattern[] patterns;
public TypeReference type;
int thenInitStateIndex1 = -1;
Expand Down Expand Up @@ -144,8 +140,6 @@ public TypeBinding resolveType(BlockScope scope) {
LocalVariableBinding [] bindings = NO_VARIABLES;
for (Pattern p : this.patterns) {
p.resolveTypeWithBindings(bindings, scope);
if (p instanceof TypePattern tp)
tp.createSecretVariable(scope, p.resolvedType);
bindings = LocalVariableBinding.merge(bindings, p.bindingsWhenTrue());
}
for (LocalVariableBinding binding : bindings)
Expand All @@ -155,8 +149,6 @@ public TypeBinding resolveType(BlockScope scope) {
return this.resolvedType;
}

createSecretVariable(scope, this.resolvedType);

this.isTotalTypeNode = super.coversType(this.resolvedType);
RecordComponentBinding[] components = this.resolvedType.capture(scope, this.sourceStart, this.sourceEnd).components();
for (int i = 0; i < components.length; i++) {
Expand Down Expand Up @@ -211,51 +203,64 @@ public boolean dominates(Pattern p) {
}
return true;
}

@Override
public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel) {
// codeStream.checkcast(this.resolvedType);
initializePatternVariables(currentScope, codeStream);
generatePatternVariable(currentScope, codeStream, trueLabel, falseLabel);
wrapupGeneration(codeStream);
if (this.thenInitStateIndex2 != -1) {
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.thenInitStateIndex2);
codeStream.addDefinitelyAssignedVariables(currentScope, this.thenInitStateIndex2);
}
}
@Override
protected void generatePatternVariable(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel) {
List<ExceptionLabel> labels = new ArrayList<>();
for (Pattern p : this.patterns) {
if (p.accessorMethod != null) {
codeStream.load(this.secretPatternVariable);
codeStream.checkcast(this.resolvedType);

ExceptionLabel exceptionLabel = new ExceptionLabel(codeStream,TypeBinding.wellKnownType(currentScope, T_JavaLangThrowable));
exceptionLabel.placeStart();
codeStream.invoke(Opcodes.OPC_invokevirtual, p.accessorMethod.original(), this.resolvedType, null);
exceptionLabel.placeEnd();
labels.add(exceptionLabel);
/* JVM Stack on entry - [expression] // && expression instanceof this.resolvedType
JVM stack on exit with successful pattern match or failed match -> []
Notation: 'R' : record pattern, 'C': component
*/
codeStream.checkcast(this.resolvedType); // [R]
List<ExceptionLabel> labels = new ArrayList<>();
for (int i = 0, length = this.patterns.length; i < length; i++) {
Pattern p = this.patterns[i];
/* For all but the last component, dup the record instance to use
as receiver for accessor invocation. The last component uses the
original record instance as receiver - leaving the stack drained.
*/
boolean lastComponent = i == length - 1;
if (!lastComponent)
codeStream.dup(); // lastComponent ? [R] : [R, R]
ExceptionLabel exceptionLabel = new ExceptionLabel(codeStream,
TypeBinding.wellKnownType(currentScope, T_JavaLangThrowable));
exceptionLabel.placeStart();
codeStream.invoke(Opcodes.OPC_invokevirtual, p.accessorMethod.original(), this.resolvedType, null);
// lastComponent ? [C] : [R, C]
exceptionLabel.placeEnd();
labels.add(exceptionLabel);

if (TypeBinding.notEquals(p.accessorMethod.original().returnType.erasure(),
p.accessorMethod.returnType.erasure()))
codeStream.checkcast(p.accessorMethod.returnType);
if (p instanceof RecordPattern || !p.isTotalTypeNode) {
((TypePattern)p).createSecretVariable(currentScope, p.resolvedType); // Looks suspect - Srikanth
((TypePattern)p).initializePatternVariables(currentScope, codeStream);
codeStream.load(p.secretPatternVariable);
codeStream.instance_of(p.resolvedType);
BranchLabel target = falseLabel != null ? falseLabel : new BranchLabel(codeStream);
List<LocalVariableBinding> deepPatternVars = getDeepPatternVariables(currentScope);
recordEndPCDeepPatternVars(deepPatternVars, codeStream.position);
codeStream.ifeq(target);
recordStartPCDeepPatternVars(deepPatternVars, codeStream.position);
p.secretPatternVariable.recordInitializationStartPC(codeStream.position);
codeStream.load(p.secretPatternVariable);
codeStream.removeVariable(p.secretPatternVariable);
if (TypeBinding.notEquals(p.accessorMethod.original().returnType.erasure(),
p.accessorMethod.returnType.erasure()))
codeStream.checkcast(p.accessorMethod.returnType); // lastComponent ? [C] : [R, C]
if (p instanceof RecordPattern || !p.isTotalTypeNode) {
codeStream.dup(); // lastComponent ? [C, C] : [R, C, C]
codeStream.instance_of(p.resolvedType); // lastComponent ? [C, boolean] : [R, C, boolean]
BranchLabel target = falseLabel != null ? falseLabel : new BranchLabel(codeStream);
BranchLabel innerTruthLabel = new BranchLabel(codeStream);
codeStream.ifne(innerTruthLabel); // lastComponent ? [C] : [R, C]
int pops = 1; // Not going to store into the component pattern binding, so need to pop, the duped value.
Pattern current = p;
RecordPattern outer = this;
while (outer != null) {
if (current.index != outer.patterns.length - 1)
pops++;
current = outer;
outer = outer.getEnclosingPattern() instanceof RecordPattern rp ? rp : null;
}
p.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel);
while (pops > 1) {
codeStream.pop2();
pops -= 2;
}
if (pops > 0)
codeStream.pop();

codeStream.goto_(target);
innerTruthLabel.place();
}
p.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel);
}

if (labels.size() > 0) {
BlockScope trapScope = codeStream.accessorExceptionTrapScopes.peek();
List<ExceptionLabel> eLabels = codeStream.patternAccessorMap.get(trapScope);
Expand All @@ -266,58 +271,12 @@ protected void generatePatternVariable(BlockScope currentScope, CodeStream codeS
}
codeStream.patternAccessorMap.put(trapScope, eLabels);
}
}

List<LocalVariableBinding> getDeepPatternVariables(BlockScope blockScope) {
class PatternVariableCollector extends ASTVisitor {
Set<LocalVariableBinding> deepPatternVariables = new HashSet<>();
@Override
public boolean visit(Pattern pattern, BlockScope blockScope1) {
if (pattern.secretPatternVariable != null)
this.deepPatternVariables.add(pattern.secretPatternVariable);
return true;
}
@Override
public boolean visit(TypePattern typePattern, BlockScope blockScope1) {
if (typePattern.secretPatternVariable != null)
this.deepPatternVariables.add(typePattern.secretPatternVariable);
LocalVariableBinding local1 = typePattern.local != null ? typePattern.local.binding : null;
if (local1 != null && local1.initializationCount > 0)
this.deepPatternVariables.add(typePattern.local.binding);
return true;
}
}
PatternVariableCollector pvc = new PatternVariableCollector();
traverse(pvc, blockScope);
pvc.deepPatternVariables.add(this.secretPatternVariable);
return new ArrayList<>(pvc.deepPatternVariables);
}
private void recordEndPCDeepPatternVars(List<LocalVariableBinding> vars, int position) {
if (vars == null)
return;
for (LocalVariableBinding v : vars) {
if (v.initializationCount > 0)
v.recordInitializationEndPC(position);
}
}
private void recordStartPCDeepPatternVars(List<LocalVariableBinding> vars, int position) {
if (vars == null)
return;
for (LocalVariableBinding v : vars) {
v.recordInitializationStartPC(position);
}
}
@Override
public void initializePatternVariables(BlockScope currentScope, CodeStream codeStream) {
super.initializePatternVariables(currentScope, codeStream);
}
@Override
public void wrapupGeneration(CodeStream codeStream) {
for (Pattern p : this.patterns) {
p.wrapupGeneration(codeStream);
if (this.thenInitStateIndex2 != -1) {
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.thenInitStateIndex2);
codeStream.addDefinitelyAssignedVariables(currentScope, this.thenInitStateIndex2);
}
super.wrapupGeneration(codeStream);
}

@Override
public void suspendVariables(CodeStream codeStream, BlockScope scope) {
codeStream.removeNotDefinitelyAssignedVariables(scope, this.thenInitStateIndex1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@
import java.util.Set;

import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.BranchLabel;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
Expand Down Expand Up @@ -90,24 +88,6 @@ public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStr
codeStream.store(localBinding, false);
localBinding.recordInitializationStartPC(codeStream.position);
}
public void initializePatternVariables(BlockScope currentScope, CodeStream codeStream) {
codeStream.addVariable(this.secretPatternVariable);
codeStream.store(this.secretPatternVariable, false);
}
@Override
protected void generatePatternVariable(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel) {
codeStream.load(this.secretPatternVariable);
LocalVariableBinding localBinding = this.local.binding;
if (!this.isTotalTypeNode)
codeStream.checkcast(localBinding.type);
this.local.generateCode(currentScope, codeStream);
codeStream.store(localBinding, false);
localBinding.recordInitializationStartPC(codeStream.position);
}
@Override
public void wrapupGeneration(CodeStream codeStream) {
codeStream.removeVariable(this.secretPatternVariable);
}
@Override
public LocalDeclaration getPatternVariable() {
return this.local;
Expand Down Expand Up @@ -203,18 +183,6 @@ public boolean visit(TypeVariableBinding typeVariable) {
return mentioned.toArray(new TypeVariableBinding[mentioned.size()]);
}

protected void createSecretVariable(BlockScope scope, TypeBinding type) {
if (this.secretPatternVariable == null) {
LocalVariableBinding l = new LocalVariableBinding(
(SECRET_PATTERN_VARIABLE_NAME + this.nestingLevel).toCharArray(), type,
ClassFileConstants.AccDefault, false);
l.setConstant(Constant.NotAConstant);
l.useFlag = LocalVariableBinding.USED;
scope.addLocalVariable(l);
this.secretPatternVariable = l;
}
}

@Override
public void traverse(ASTVisitor visitor, BlockScope scope) {
if (visitor.visit(this, scope)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,6 @@ public int getFrameType(StackMapFrame prevFrame) {
return FULL_FRAME;
}

public void addLocal(int resolvedPosition, VerificationTypeInfo info) {
if (this.locals == null) {
this.locals = new VerificationTypeInfo[resolvedPosition + 1];
this.locals[resolvedPosition] = info;
} else {
final int length = this.locals.length;
if (resolvedPosition >= length) {
System.arraycopy(this.locals, 0, this.locals = new VerificationTypeInfo[resolvedPosition + 1], 0,
length);
}
this.locals[resolvedPosition] = info;
}
}

public void addStackItem(VerificationTypeInfo info) {
if (info == null) {
throw new IllegalArgumentException("info cannot be null"); //$NON-NLS-1$
Expand Down
Loading

0 comments on commit 8004cb6

Please sign in to comment.