Skip to content

Commit

Permalink
8273408: java.lang.AssertionError: typeSig ERROR on generated class p…
Browse files Browse the repository at this point in the history
…roperty of record

Reviewed-by: vromero
  • Loading branch information
lgxbslgx committed Sep 17, 2021
1 parent 8c022e2 commit e07ab82
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1504,16 +1504,30 @@ public RecordComponent getRecordComponent(VarSymbol field) {
}

public RecordComponent getRecordComponent(JCVariableDecl var, boolean addIfMissing, List<JCAnnotation> annotations) {
RecordComponent toRemove = null;
for (RecordComponent rc : recordComponents) {
/* it could be that a record erroneously declares two record components with the same name, in that
* case we need to use the position to disambiguate
*/
if (rc.name == var.name && var.pos == rc.pos) {
return rc;
if (rc.type.hasTag(TypeTag.ERROR) && !var.sym.type.hasTag(TypeTag.ERROR)) {
// Found a record component with an erroneous type: save it so that it can be removed later.
// If the class type of the record component is generated by annotation processor, it should
// use the new actual class type and symbol instead of the old dummy ErrorType.
toRemove = rc;
} else {
// Found a good record component: just return.
return rc;
}
}
}
RecordComponent rc = null;
if (addIfMissing) {
if (toRemove != null) {
// Found a record component with an erroneous type: remove it and create a new one
recordComponents = List.filter(recordComponents, toRemove);
recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, annotations));
} else if (addIfMissing) {
// Didn't find the record component: create one.
recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, annotations));
}
return rc;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Set;

@SupportedAnnotationTypes("*")
public class GenerateTypeProcessor extends AbstractProcessor {

private String code = "public class GeneratedType { }";

private boolean firstTime = true;

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// Create a new class: GeneratedType
if (firstTime) {
try (OutputStream out =
processingEnv.getFiler().createSourceFile("GeneratedType").openOutputStream()) {
out.write(code.getBytes());
} catch (IOException ex) {
ex.printStackTrace();
}
firstTime = false;
}
return false;
}

@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

/*
* @test
* @bug 8273408
* @summary The compiler shouldn't crash when record component uses the class generated by the annotation processor.
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* jdk.jdeps/com.sun.tools.classfile
* @compile GenerateTypeProcessor.java
* @run main RecordComponentTypeTest
*/

import com.sun.tools.classfile.*;

import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;

import toolbox.JavacTask;
import toolbox.TestRunner;
import toolbox.ToolBox;
import toolbox.Task;

public class RecordComponentTypeTest extends TestRunner {

ToolBox tb;
ClassFile cf;

public RecordComponentTypeTest() {
super(System.err);
tb = new ToolBox();
}

public static void main(String[] args) throws Exception {
RecordComponentTypeTest t = new RecordComponentTypeTest();
t.runTests();
}

@Test
public void testRecordComponentUsingGeneratedType() throws Exception {
String code = "public record RecordComponentUsingGeneratedType(GeneratedType generatedType) { }";
Path curPath = Path.of(".");

// Have no annotation processor.
List<String> output = new JavacTask(tb)
.sources(code)
.outdir(curPath)
.options("-XDrawDiagnostics")
.run(Task.Expect.FAIL)
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
List<String> expected = Arrays.asList(
"RecordComponentUsingGeneratedType.java:1:49: compiler.err.cant.resolve.location: kindname.class, " +
"GeneratedType, , , (compiler.misc.location: kindname.class, RecordComponentUsingGeneratedType, null)",
"1 error");
tb.checkEqual(expected, output);

// Have annotation processor, and processor generates expected type.
new JavacTask(tb)
.sources(code)
.options("-processor", "GenerateTypeProcessor")
.outdir(curPath)
.run();
}

@Test
public void testRecordComponentUsingUnknownType() throws Exception {
String code = "public record RecordComponentUsingUnknownType(UnknownType unknownType) { }";
Path curPath = Path.of(".");

// Have no annotation processor.
List<String> output = new JavacTask(tb)
.sources(code)
.outdir(curPath)
.options("-XDrawDiagnostics")
.run(Task.Expect.FAIL)
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
List<String> expected = Arrays.asList(
"RecordComponentUsingUnknownType.java:1:47: compiler.err.cant.resolve.location: kindname.class, " +
"UnknownType, , , (compiler.misc.location: kindname.class, RecordComponentUsingUnknownType, null)",
"1 error");
tb.checkEqual(expected, output);

// Have annotation processor, but processor doesn't generate the expected type.
List<String> output2 = new JavacTask(tb)
.sources(code)
.outdir(curPath)
.options("-XDrawDiagnostics", "-processor", "GenerateTypeProcessor")
.run(Task.Expect.FAIL)
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
List<String> expected2 = Arrays.asList(
"RecordComponentUsingUnknownType.java:1:47: compiler.err.cant.resolve.location: kindname.class, " +
"UnknownType, , , (compiler.misc.location: kindname.class, RecordComponentUsingUnknownType, null)",
"1 error");
tb.checkEqual(expected2, output2);
}


@Test
public void testRecordComponentUsingGeneratedTypeWithAnnotation() throws Exception {
String code = """
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public record RecordComponentUsingGeneratedTypeWithAnnotation(@TestAnnotation GeneratedType generatedType) { }
@Retention(RetentionPolicy.RUNTIME)
@interface TestAnnotation { }
""";
Path curPath = Path.of(".");
new JavacTask(tb)
.sources(code)
.options("-processor", "GenerateTypeProcessor")
.outdir(curPath)
.run();
cf = ClassFile.read(curPath.resolve("RecordComponentUsingGeneratedTypeWithAnnotation.class"));

for (Field field : cf.fields) {
if ("generatedType".equals(field.getName(cf.constant_pool))) {
checkRuntimeVisibleAnnotation(field.attributes);
}
}

for (Method method : cf.methods) {
if ("generatedType".equals(method.getName(cf.constant_pool))) {
checkRuntimeVisibleAnnotation(method.attributes);
}
}
}

private void checkRuntimeVisibleAnnotation(Attributes attributes) throws Exception {
RuntimeVisibleAnnotations_attribute annotations =
(RuntimeVisibleAnnotations_attribute) attributes.get(Attribute.RuntimeVisibleAnnotations);
boolean hasAnnotation = false;
for (Annotation annotation : annotations.annotations) {
if ("LTestAnnotation;".equals(cf.constant_pool.getUTF8Value(annotation.type_index))) {
hasAnnotation = true;
}
}
if (!hasAnnotation) {
throw new AssertionError("Expected RuntimeVisibleAnnotation doesn't appear");
}
}
}

0 comments on commit e07ab82

Please sign in to comment.