diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index 1ef263c04bd..7fbff3b3254 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -1015,7 +1015,9 @@ void writeMethod(MethodSymbol m) { endAttr(alenIdx); acount++; } - if (target.hasMethodParameters() && (options.isSet(PARAMETERS) || m.isConstructor() && (m.flags_field & RECORD) != 0)) { + if (target.hasMethodParameters() && ( + options.isSet(PARAMETERS) + || ((m.flags_field & RECORD) != 0 && (m.isConstructor() || m.isPrimitiveObjectFactory())))) { if (!m.isLambdaMethod()) // Per JDK-8138729, do not emit parameters table for lambda bodies. acount += writeMethodParametersAttr(m); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/TransPrimitiveClass.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/TransPrimitiveClass.java index 071dff118fd..160276a7312 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/TransPrimitiveClass.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/TransPrimitiveClass.java @@ -389,7 +389,7 @@ private MethodSymbol getPrimitiveObjectFactory(MethodSymbol init) { if (factory != null) return factory; - MethodType factoryType = new MethodType(init.erasure(types).getParameterTypes(), + MethodType factoryType = new MethodType(init.type.getParameterTypes(), init.owner.type.asValueType(), init.type.getThrownTypes(), init.owner.type.tsym); @@ -397,6 +397,12 @@ private MethodSymbol getPrimitiveObjectFactory(MethodSymbol init) { names.init, factoryType, init.owner); + factory.params = init.params; + // Re-patch the return type on the erased method type, or code generation will fail + factory.erasure_field = new MethodType(init.erasure(types).getParameterTypes(), + init.owner.type.asValueType(), + init.type.getThrownTypes(), + init.owner.type.tsym); factory.setAttributes(init); init2factory.put(init, factory); return factory; diff --git a/test/langtools/tools/javac/records/RecordReading.java b/test/langtools/tools/javac/records/RecordReading.java index 6133a04fca9..a3f01410251 100644 --- a/test/langtools/tools/javac/records/RecordReading.java +++ b/test/langtools/tools/javac/records/RecordReading.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -25,6 +25,7 @@ * @test * @summary test the records can be read by javac properly * @library /tools/lib + * @bug 8273018 * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main * @build toolbox.ToolBox toolbox.JavacTask @@ -126,4 +127,67 @@ public R(int i, } } + @Test + public void testPrimitiveRecordClassFileReading(Path base) throws Exception { + Path src = base.resolve("src"); + + tb.writeJavaFiles(src, + """ + public primitive record R(int i, @A long j, java.util.List l) {} + """, + """ + public @interface A {} + """); + + Path out = base.resolve("out"); + Files.createDirectories(out); + + new JavacTask(tb) + .outdir(out) + .files(findJavaFiles(src)) + .run(); + + //read the class file back, to verify javac's ClassReader + //reads the Record attribute properly: + String output = new JavacTask(tb) + .options("-Xprint") + .classpath(out.toString()) + .classes("R") + .run() + .writeAll() + .getOutput(Task.OutputKind.STDOUT) + .replaceAll("\\R", "\n"); + + String expected = + """ + \n\ + public primitive record R(int i, @A long j, java.util.List l) { + private final int i; + @A + private final long j; + private final java.util.List l; + \n\ + public final java.lang.String toString(); + \n\ + public final int hashCode(); + \n\ + public final boolean equals(java.lang.Object arg0); + \n\ + public int i(); + \n\ + @A + public long j(); + \n\ + public java.util.List l(); + \n\ + public R(int i, + @A long j, + java.util.List l); + } + """; + if (!Objects.equals(expected, output)) { + throw new AssertionError("Unexpected output: " + output); + } + } + } diff --git a/test/langtools/tools/javac/valhalla/lworld-values/records/ApplicableAnnotationsOnPrimitiveRecords.java b/test/langtools/tools/javac/valhalla/lworld-values/records/ApplicableAnnotationsOnPrimitiveRecords.java new file mode 100644 index 00000000000..f7489720309 --- /dev/null +++ b/test/langtools/tools/javac/valhalla/lworld-values/records/ApplicableAnnotationsOnPrimitiveRecords.java @@ -0,0 +1,84 @@ +/* + * 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 + * @summary [lworld] test for equal treatment of annotations on primitive records (copy of ApplicableAnnotationsOnRecords) + * @bug 8273018 + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.util + * jdk.jdeps/com.sun.tools.classfile + * @run main ApplicableAnnotationsOnPrimitiveRecords + */ +import com.sun.tools.classfile.*; +import com.sun.tools.javac.util.Assert; +import java.lang.annotation.*; +import java.io.InputStream; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +@interface FieldAnnotation { +} + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +@interface MethodAnnotation { +} + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PARAMETER}) +@interface ParameterAnnotation { +} + +public primitive record ApplicableAnnotationsOnPrimitiveRecords(@FieldAnnotation @MethodAnnotation @ParameterAnnotation String s, @FieldAnnotation @MethodAnnotation @ParameterAnnotation int i) { + + public static void main(String... args) throws Exception { + try ( InputStream in = ApplicableAnnotationsOnPrimitiveRecords.class.getResourceAsStream("ApplicableAnnotationsOnPrimitiveRecords.class")) { + ClassFile cf = ClassFile.read(in); + Assert.check(cf.methods.length > 5); + for (Method m : cf.methods) { + String methodName = m.getName(cf.constant_pool); + if (methodName.equals("toString") || methodName.equals("hashCode") || methodName.equals("equals") || methodName.equals("main")) { + // ignore + } else if (methodName.equals("")) { + var paAnnos = ((RuntimeVisibleParameterAnnotations_attribute) m.attributes.get(Attribute.RuntimeVisibleParameterAnnotations)).parameter_annotations; + Assert.check(paAnnos != null && paAnnos.length > 0); + for (var pa : paAnnos) { + Assert.check(pa.length == 1); + Assert.check(cf.constant_pool.getUTF8Value(pa[0].type_index).equals("LParameterAnnotation;")); + } + } else { + var annos = ((RuntimeAnnotations_attribute) m.attributes.get(Attribute.RuntimeVisibleAnnotations)).annotations; + Assert.check(annos.length == 1); + Assert.check(cf.constant_pool.getUTF8Value(annos[0].type_index).equals("LMethodAnnotation;")); + } + } + Assert.check(cf.fields.length > 0); + for (Field field : cf.fields) { + var annos = ((RuntimeAnnotations_attribute) field.attributes.get(Attribute.RuntimeVisibleAnnotations)).annotations; + Assert.check(annos.length == 1); + Assert.check(cf.constant_pool.getUTF8Value(annos[0].type_index).equals("LFieldAnnotation;")); + } + } + } +}