Skip to content

Commit

Permalink
Record with exception in constructor results in invalid byte code
Browse files Browse the repository at this point in the history
The following code results in generating instructions after an athrow
byte code statement:

record X(String s) {
    X {
        throw new RuntimeException();
    }
}

This is due to
CompactConstructorDeclaration.checkAndGenerateFieldAssignment()
appending field assignments after the record constructor body.

This change adjusts CompactConstructorDeclaration to not generate field
assignments if the execution flow is unreachable or dead after adding
the statements from the record constructor. This prevents generating
code after an athrow statement in the byte code.

Fixes: eclipse-jdt#487
Signed-off-by: Simeon Andreev <[email protected]>
  • Loading branch information
trancexpress authored and iloveeclipse committed Mar 30, 2023
1 parent b681dfb commit c9c2eb9
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ protected void checkAndGenerateFieldAssignment(FlowContext flowContext, FlowInfo
assert flowInfo.isDefinitelyAssigned(field);
fieldAssignments.add(assignment);
}
if (fieldAssignments.isEmpty())
if (fieldAssignments.isEmpty() || (flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0)
return;

Statement[] fa = fieldAssignments.toArray(new Statement[0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9213,4 +9213,58 @@ public void testIssue365_001() throws Exception {
RecordsRestrictedClassTest.verifyClassFile(expectedOutput, "A.class", ClassFileBytesDisassembler.SYSTEM);

}

/**
* Test that the following code doesn't result in generating byte code after the throw statement:
* <pre>
* record X(String s) {
* X {
* throw new RuntimeException();
* }
* }
* </pre>
*/
public void testRecordConstructorWithExceptionGh487() throws Exception {
getPossibleComplianceLevels();
runConformTest(
// test directory preparation
true /* should flush output directory */,
new String[] { /* test files */
"X.java",
"""
public record X(String s) {
public X {
throw new RuntimeException();
}
public static void main(String[] args) throws Exception {
new X("");
}
}
""",
},
// compiler results
"" /* expected compiler log */,
// runtime results
"" /* expected output string */,
"""
java.lang.RuntimeException
at X.<init>(X.java:3)
at X.main(X.java:6)
""" /* expected error string */,
// javac options
JavacTestOptions.forRelease("16"));
String expectedOutput = // constructor
"""
// Method descriptor #8 (Ljava/lang/String;)V
// Stack: 2, Locals: 2
public X(java.lang.String s);
0 aload_0 [this]
1 invokespecial java.lang.Record() [10]
4 new java.lang.RuntimeException [13]
7 dup
8 invokespecial java.lang.RuntimeException() [15]
11 athrow
""";
RecordsRestrictedClassTest.verifyClassFile(expectedOutput, "X.class", ClassFileBytesDisassembler.SYSTEM);
}
}

0 comments on commit c9c2eb9

Please sign in to comment.