diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java index d63ff326153..f2c3c00d32c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java @@ -1429,6 +1429,14 @@ public boolean isSubClass(Symbol base, Types types) { return false; } + /** + * Does `this' symbolize a primitive class that would, under the translation + * scheme in effect be lowered into two class files on a bifurcased basis ?? + */ + public boolean isSplitPrimitiveClass(Types types) { + return types.splitPrimitiveClass && this.isPrimitiveClass(); + } + /** Complete the elaboration of this symbol's definition. */ public void complete() throws CompletionFailure { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 3b4f6418d3d..40d796d47fc 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -101,6 +101,18 @@ public class Types { List warnStack = List.nil(); final Name capturedName; + /** + * If true, the ClassWriter will split a primitive class declaration into two class files + * P.ref.class and P.val.class (P.class for pure primitive classes) + * + * This is the default behavior, can be eoverridden with -XDunifiedValRefClass + * + * If false, we emit a single class for a primtive class 'P' and the reference projection and + * value projection types are encoded in descriptors as LP; and QP; resperctively. + */ + + public boolean splitPrimitiveClass; + public final Warner noWarnings; // @@ -126,6 +138,7 @@ protected Types(Context context) { noWarnings = new Warner(null); Options options = Options.instance(context); allowValueBasedClasses = options.isSet("allowValueBasedClasses"); + splitPrimitiveClass = options.isUnset("unifiedValRefClass"); } // @@ -5353,7 +5366,7 @@ public void assembleClassSig(Type type) { } else { append(externalize(c.flatname)); } - if (ct.isReferenceProjection()) { + if (types.splitPrimitiveClass && ct.isReferenceProjection()) { append('$'); append(types.names.ref); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index 9045d20eaca..0acb83d079b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -3108,12 +3108,15 @@ T applyPrimitiveConversionsAsNeeded(T tree, Type type) if (haveValue == type.isPrimitiveClass()) return tree; if (haveValue) { - // widening coversion is a NOP for the VM due to subtyping relationship at class file - return tree; - } else { - // For narrowing conversion, insert a cast which should trigger a null check - return (T) make.TypeCast(type, tree); + // widening coversion is a NOP for the VM due to subtyping relationship at class file level + // where we bifurcate a primitive class into two class files. + if (types.splitPrimitiveClass) + return tree; } + // For narrowing conversion, insert a cast which should trigger a null check + // For widening conversions, insert a cast if emitting a unified class file. + return (T) make.TypeCast(type, tree); + } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 9825e919998..e3b9cac6c01 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -2687,7 +2687,7 @@ public void readClassFile(ClassSymbol c) { ClassSymbol referenceProjection = syms.getClass(currentModule, flatname); if (referenceProjection != null) { if (referenceProjection.name != names.ref && referenceProjection.owner.kind == PCK) { - readClassFileInternal(referenceProjection); + referenceProjection.complete(); ClassType classType = (ClassType) c.type; classType.supertype_field = ((ClassType) referenceProjection.type).supertype_field; classType.interfaces_field = ((ClassType) referenceProjection.type).interfaces_field; 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 c120ef7b56c..3dd733a67ae 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 @@ -845,7 +845,7 @@ void writeInnerClasses() { !inner.name.isEmpty() ? poolWriter.putName(inner.name) : 0); databuf.appendChar(flags); icCount++; - if (inner.isPrimitiveClass()) { + if (inner.isSplitPrimitiveClass(types)) { databuf.appendChar(poolWriter.putClass(inner.type.referenceProjection())); databuf.appendChar( inner.owner.kind == TYP && !inner.name.isEmpty() ? poolWriter.putClass((ClassSymbol)inner.owner) : 0); @@ -883,7 +883,7 @@ int writeRecordAttribute(ClassSymbol csym) { int writeNestMembersIfNeeded(ClassSymbol csym) { Set nestedUnique = new LinkedHashSet<>(); if (csym.owner.kind == PCK) { - if (csym.isPrimitiveClass()) { + if (csym.isSplitPrimitiveClass(types)) { // reference projection is the host } else if (csym.isReferenceProjection()) { ClassSymbol valueProjection = csym.valueProjection(); @@ -899,7 +899,7 @@ int writeNestMembersIfNeeded(ClassSymbol csym) { for (ClassSymbol s : nestedUnique) { databuf.appendChar(poolWriter.putClass(s)); nmc++; - if (s.isPrimitiveClass() && s.owner.kind != PCK) { + if (s.isSplitPrimitiveClass(types) && s.owner.kind != PCK) { databuf.appendChar(poolWriter.putClass(s.type.referenceProjection())); nmc++; } @@ -916,10 +916,10 @@ int writeNestMembersIfNeeded(ClassSymbol csym) { * Write NestHost attribute (if needed) */ int writeNestHostIfNeeded(ClassSymbol csym) { - if (csym.owner.kind != PCK || csym.isPrimitiveClass()) { + if (csym.owner.kind != PCK || csym.isSplitPrimitiveClass(types)) { int alenIdx = writeAttr(names.NestHost); ClassSymbol outerMost = csym.outermostClass(); - if (outerMost.isPrimitiveClass()) { + if (outerMost.isSplitPrimitiveClass(types)) { databuf.appendChar(poolWriter.putClass(outerMost.type.referenceProjection())); } else { databuf.appendChar(poolWriter.putClass(outerMost)); @@ -1530,7 +1530,7 @@ public JavaFileObject writeClass(ClassSymbol c) throws IOException, PoolOverflow, StringOverflow { JavaFileObject javaFileObject = writeClassInternal(c); - if (c.isPrimitiveClass()) { + if (c.isSplitPrimitiveClass(types)) { writeClassInternal(getReferenceProjection(c)); } return javaFileObject; @@ -1631,8 +1631,8 @@ public void writeClassFile(OutputStream out, ClassSymbol c) databuf.reset(); poolbuf.reset(); - Type supertype = c.isPrimitiveClass() ? c.type.referenceProjection() : types.supertype(c.type); - List interfaces = c.isPrimitiveClass() ? List.nil() : types.interfaces(c.type); + Type supertype = c.isSplitPrimitiveClass(types) ? c.type.referenceProjection() : types.supertype(c.type); + List interfaces = c.isSplitPrimitiveClass(types) ? List.nil() : types.interfaces(c.type); List typarams = c.type.getTypeArguments(); int flags; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java index 0822f0f8f63..1c4b28ad0b7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java @@ -399,6 +399,17 @@ void postop() { /** Emit a ldc (or ldc_w) instruction, taking into account operand size */ + public void emitLdc(LoadableConstant constant, int od) { + if (od <= 255) { + emitop1(ldc1, od, constant); + } + else { + emitop2(ldc2, od, constant); + } + } + + /** Emit a ldc (or ldc_w) instruction, taking into account operand size + */ public void emitLdc(LoadableConstant constant) { int od = poolWriter.putConstant(constant); if (od <= 255) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index c0da0b39473..0f213cfcffc 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -2272,10 +2272,10 @@ public void visitTypeCast(JCTypeCast tree) { // which is not statically a supertype of the expression's type. // For basic types, the coerce(...) in genExpr(...) will do // the conversion. - // inline widening conversion is a nop, as the VM sees a subtyping relationship. + // inline widening conversion is a nop when we bifurcate the primitive class, as the VM sees a subtyping relationship. if (!tree.clazz.type.isPrimitive() && !types.isSameType(tree.expr.type, tree.clazz.type) && - (!tree.clazz.type.isReferenceProjection() || !types.isSameType(tree.clazz.type.valueProjection(), tree.expr.type)) && + (!tree.clazz.type.isReferenceProjection() || !types.splitPrimitiveClass || !types.isSameType(tree.clazz.type.valueProjection(), tree.expr.type)) && !types.isSubtype(tree.expr.type, tree.clazz.type)) { checkDimension(tree.pos(), tree.clazz.type); if (types.isPrimitiveClass(tree.clazz.type)) { @@ -2345,7 +2345,7 @@ public void visitSelect(JCFieldAccess tree) { Symbol sym = tree.sym; if (tree.name == names._class) { - code.emitLdc((LoadableConstant)checkDimension(tree.pos(), tree.selected.type)); + code.emitLdc((LoadableConstant) tree.selected.type, makeRef(tree.pos(), tree.selected.type, !types.splitPrimitiveClass && tree.selected.type.isPrimitiveClass())); result = items.makeStackItem(pt); return; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java index 893aa0bb49a..7371cb644ae 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java @@ -378,7 +378,7 @@ void writeConstant(PoolConstant c) { Name name = ct.hasTag(ARRAY) ? typeSig(ct) : c instanceof ConstantPoolQType ? names.fromString("Q" + new String(externalize(ct.tsym.flatName())) + ";") : names.fromUtf(externalize(ct.tsym.flatName())); - if (ct.isReferenceProjection()) { + if (types.splitPrimitiveClass && ct.isReferenceProjection()) { name = name.append('$', names.ref); } poolbuf.appendByte(tag); @@ -514,8 +514,8 @@ private Name classSig(Type t) { if (typarams.nonEmpty()) { signatureGen.assembleParamsSig(typarams); } - signatureGen.assembleSig(t.isPrimitiveClass() ? t.referenceProjection() : types.supertype(t)); - if (!t.isPrimitiveClass()) { + signatureGen.assembleSig(t.isPrimitiveClass() && types.splitPrimitiveClass ? t.referenceProjection() : types.supertype(t)); + if (!t.isPrimitiveClass() || !types.splitPrimitiveClass) { for (Type i : types.interfaces(t)) signatureGen.assembleSig(i); } diff --git a/test/langtools/tools/javac/valhalla/lworld-values/SplitPrimitiveClassBytecodeTest.java b/test/langtools/tools/javac/valhalla/lworld-values/SplitPrimitiveClassBytecodeTest.java new file mode 100644 index 00000000000..356a3bc39b7 --- /dev/null +++ b/test/langtools/tools/javac/valhalla/lworld-values/SplitPrimitiveClassBytecodeTest.java @@ -0,0 +1,118 @@ +/* + * 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 8265423 + * @summary Experimental support for generating a single class file per primitive class + * @modules jdk.compiler/com.sun.tools.javac.util jdk.jdeps/com.sun.tools.javap + * @compile SplitPrimitiveClassBytecodeTest.java + * @run main SplitPrimitiveClassBytecodeTest + * @modules jdk.compiler + */ + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Paths; + +public class SplitPrimitiveClassBytecodeTest { + + public primitive class X { + + X.ref xr = null; + + public void foo(X.ref[] xra, X[] xa) { + xa = new X[10]; + xra = new X.ref[10]; + xra[0] = xa[0]; + xa[1] = xra[0]; + Class c = X.class; + c = X.ref.class; + } + } + + public static void main(String[] args) { + new SplitPrimitiveClassBytecodeTest().run(); + } + + void run() { + String [] params = new String [] { "-v", + Paths.get(System.getProperty("test.classes"), + "SplitPrimitiveClassBytecodeTest$X.class").toString() }; + runCheck(params, new String [] { + + // check field + "final SplitPrimitiveClassBytecodeTest$X$ref xr;", + "descriptor: LSplitPrimitiveClassBytecodeTest$X$ref;", + "flags: (0x0010) ACC_FINAL", + + // check method + "public void foo(SplitPrimitiveClassBytecodeTest$X$ref[], SplitPrimitiveClassBytecodeTest$X[]);", + "descriptor: ([LSplitPrimitiveClassBytecodeTest$X$ref;[QSplitPrimitiveClassBytecodeTest$X;)V", + " 0: bipush 10", + " 2: anewarray #11 // class \"QSplitPrimitiveClassBytecodeTest$X;\"", + " 5: astore_2", + " 6: bipush 10", + " 8: anewarray #13 // class SplitPrimitiveClassBytecodeTest$X$ref", + "11: astore_1", + "12: aload_1", + "13: iconst_0", + "14: aload_2", + "15: iconst_0", + "16: aaload", + "17: aastore", + "18: aload_2", + "19: iconst_1", + "20: aload_1", + "21: iconst_0", + "22: aaload", + "23: checkcast #11 // class \"QSplitPrimitiveClassBytecodeTest$X;\"", + "26: aastore", + "27: ldc #1 // class SplitPrimitiveClassBytecodeTest$X", + "29: astore_3", + "30: ldc #13 // class SplitPrimitiveClassBytecodeTest$X$ref", + "32: astore_3", + "33: return", + }); + } + + void runCheck(String [] params, String [] expectedOut) { + StringWriter s; + String out; + + try (PrintWriter pw = new PrintWriter(s = new StringWriter())) { + com.sun.tools.javap.Main.run(params, pw); + out = s.toString(); + } + int errors = 0; + for (String eo: expectedOut) { + if (!out.contains(eo)) { + System.err.println("Match not found for string: " + eo); + errors++; + } + } + if (errors > 0) { + throw new AssertionError("Unexpected javap output: " + out); + } + } +} diff --git a/test/langtools/tools/javac/valhalla/lworld-values/SplitPrimitiveClassInnerClassesTest.java b/test/langtools/tools/javac/valhalla/lworld-values/SplitPrimitiveClassInnerClassesTest.java new file mode 100644 index 00000000000..e2c5ec67833 --- /dev/null +++ b/test/langtools/tools/javac/valhalla/lworld-values/SplitPrimitiveClassInnerClassesTest.java @@ -0,0 +1,73 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 8265423 + * @summary Experimental support for generating a single class file per primitive class + * @modules jdk.jdeps/com.sun.tools.classfile + * @run main SplitPrimitiveClassInnerClassesTest + */ + +import com.sun.tools.classfile.*; +import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info; + +public class SplitPrimitiveClassInnerClassesTest { + + primitive class V implements java.io.Serializable {} + + public static void main(String[] args) throws Exception { + ClassFile cls = ClassFile.read(SplitPrimitiveClassInnerClassesTest.class.getResourceAsStream("SplitPrimitiveClassInnerClassesTest.class")); + + if (cls == null) { + throw new AssertionError("Could not locate the class files"); + } + + /* Check emission of inner class attribute */ + InnerClasses_attribute inners = (InnerClasses_attribute) cls.attributes.get(Attribute.InnerClasses); + if (inners == null) { + throw new AssertionError("Missing inner class attribute"); + } + boolean foundV = false, foundVref = false; + for (int i = 0; i < inners.number_of_classes; i++) { + String name = inners.classes[i].getInnerName(cls.constant_pool); + if (name.equals("V")) + foundV = true; + else if (name.equals("V$ref")) + foundVref = true; + } + if (!foundV || !foundVref) { + throw new AssertionError("Incorrect inner class attribute"); + } + + // Test signature attribute + cls = ClassFile.read(SplitPrimitiveClassInnerClassesTest.class.getResourceAsStream("SplitPrimitiveClassInnerClassesTest$V.class")); + Signature_attribute signature = (Signature_attribute)cls.attributes.get(Attribute.Signature); + String sign = signature.getSignature(cls.constant_pool); + if (sign == null || !sign.equals("LSplitPrimitiveClassInnerClassesTest$V$ref;")) { + throw new RuntimeException("Wrong signature " + sign); + } + } +} diff --git a/test/langtools/tools/javac/valhalla/lworld-values/SplitPrimitiveClassNestHostTest.java b/test/langtools/tools/javac/valhalla/lworld-values/SplitPrimitiveClassNestHostTest.java new file mode 100644 index 00000000000..099d385c7ad --- /dev/null +++ b/test/langtools/tools/javac/valhalla/lworld-values/SplitPrimitiveClassNestHostTest.java @@ -0,0 +1,104 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 8265423 + * @summary Experimental support for generating a single class file per primitive class + * @compile SplitPrimitiveClassNestHostTest.java + * @run main SplitPrimitiveClassNestHostTest + */ + +import java.util.Arrays; + +public primitive class SplitPrimitiveClassNestHostTest implements java.io.Serializable { + + primitive class Inner {} + + public static void main(String [] args) { + + // check wiring of super types. + Class superClass = SplitPrimitiveClassNestHostTest.class.getSuperclass(); + if (!superClass.equals( + SplitPrimitiveClassNestHostTest.class.referenceType().get())) + throw new AssertionError("Wrong superclass for SplitPrimitiveClassNestHostTest"); + + Class [] superInterfaces = SplitPrimitiveClassNestHostTest.class.getInterfaces(); + if (superInterfaces.length != 1) + throw new AssertionError("Wrong super interfaces for SplitPrimitiveClassNestHostTest"); + + if (!superInterfaces[0].equals(PrimitiveObject.class)) + throw new AssertionError("Wrong super interfaces for SplitPrimitiveClassNestHostTest"); + + // check super types of ref.class + if (!superClass.getSuperclass().equals(Object.class)) + throw new AssertionError("Wrong superclass for SplitPrimitiveClassNestHostTest.ref"); + superInterfaces = superClass.getInterfaces(); + if (superInterfaces.length != 1) + throw new AssertionError("Wrong super interfaces for SplitPrimitiveClassNestHostTest.ref"); + + if (!superInterfaces[0].equals(java.io.Serializable.class)) + throw new AssertionError("Wrong super interfaces for SplitPrimitiveClassNestHostTest.ref"); + + + Class nestHost = SplitPrimitiveClassNestHostTest.class.getNestHost(); + if (!nestHost.equals(SplitPrimitiveClassNestHostTest.class.referenceType().get())) + throw new AssertionError("Wrong nest host: " + nestHost); + + Class [] members = nestHost.getNestMembers(); + if (members.length != 4) + throw new AssertionError("Wrong member count: " + members.length); + + if (!members[0].equals(nestHost)) + throw new AssertionError("Wrong initial member: " + members[0]); + + if (!members[1].equals(SplitPrimitiveClassNestHostTest.class)) + throw new AssertionError("Wrong member[1]: " + members[1]); + + if (!members[1].getNestHost().equals(nestHost)) + throw new AssertionError("Wrong nest host for member[1]: " + members[1]); + + if (!Arrays.equals(members[1].getNestMembers(), members)) + throw new AssertionError("Wrong nest members for member[1]: " + members[1]); + + if (!members[2].equals(Inner.class)) + throw new AssertionError("Wrong member[2]: " + members[2]); + + if (!members[2].getNestHost().equals(nestHost)) + throw new AssertionError("Wrong nest host for member[2]: " + members[2]); + + if (!Arrays.equals(members[2].getNestMembers(), members)) + throw new AssertionError("Wrong nest members for member[2]: " + members[2]); + + if (!members[3].equals(Inner.class.referenceType().get())) + throw new AssertionError("Wrong member[3]: " + members[3]); + + if (!members[3].getNestHost().equals(nestHost)) + throw new AssertionError("Wrong nest host for member[3]: " + members[3]); + + if (!Arrays.equals(members[3].getNestMembers(), members)) + throw new AssertionError("Wrong nest members for member[3]: " + members[3]); + } +} diff --git a/test/langtools/tools/javac/valhalla/lworld-values/UnifiedPrimitiveClassBytecodeTest.java b/test/langtools/tools/javac/valhalla/lworld-values/UnifiedPrimitiveClassBytecodeTest.java new file mode 100644 index 00000000000..cdab8a400d3 --- /dev/null +++ b/test/langtools/tools/javac/valhalla/lworld-values/UnifiedPrimitiveClassBytecodeTest.java @@ -0,0 +1,119 @@ +/* + * 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 8265423 + * @summary Experimental support for generating a single class file per primitive class + * @modules jdk.compiler/com.sun.tools.javac.util jdk.jdeps/com.sun.tools.javap + * @compile -XDunifiedValRefClass UnifiedPrimitiveClassBytecodeTest.java + * @run main UnifiedPrimitiveClassBytecodeTest + * @modules jdk.compiler + */ + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Paths; + +public class UnifiedPrimitiveClassBytecodeTest { + + public primitive class X { + + X.ref xr = null; + + public void foo(X.ref[] xra, X[] xa) { + xa = new X[10]; + xra = new X.ref[10]; + xra[0] = xa[0]; + xa[1] = xra[0]; + Class c = X.class; + c = X.ref.class; + } + } + + public static void main(String[] args) { + new UnifiedPrimitiveClassBytecodeTest().run(); + } + + void run() { + String [] params = new String [] { "-v", + Paths.get(System.getProperty("test.classes"), + "UnifiedPrimitiveClassBytecodeTest$X.class").toString() }; + runCheck(params, new String [] { + + // check field + "final UnifiedPrimitiveClassBytecodeTest$X xr;", + "descriptor: LUnifiedPrimitiveClassBytecodeTest$X;", + "flags: (0x0010) ACC_FINAL", + + // check method + "public void foo(UnifiedPrimitiveClassBytecodeTest$X[], UnifiedPrimitiveClassBytecodeTest$X[]);", + "descriptor: ([LUnifiedPrimitiveClassBytecodeTest$X;[QUnifiedPrimitiveClassBytecodeTest$X;)V", + " 0: bipush 10", + " 2: anewarray #11 // class \"QUnifiedPrimitiveClassBytecodeTest$X;\"", + " 5: astore_2", + " 6: bipush 10", + " 8: anewarray #13 // class UnifiedPrimitiveClassBytecodeTest$X", + "11: astore_1", + "12: aload_1", + "13: iconst_0", + "14: aload_2", + "15: iconst_0", + "16: aaload", + "17: checkcast #13 // class UnifiedPrimitiveClassBytecodeTest$X", + "20: aastore", + "21: aload_2", + "22: iconst_1", + "23: aload_1", + "24: iconst_0", + "25: aaload", + "26: checkcast #11 // class \"QUnifiedPrimitiveClassBytecodeTest$X;\"", + "29: aastore", + "30: ldc #11 // class \"QUnifiedPrimitiveClassBytecodeTest$X;\"", + "32: astore_3", + "33: ldc #13 // class UnifiedPrimitiveClassBytecodeTest$X", + "35: astore_3", + "36: return", + }); + } + + void runCheck(String [] params, String [] expectedOut) { + StringWriter s; + String out; + + try (PrintWriter pw = new PrintWriter(s = new StringWriter())) { + com.sun.tools.javap.Main.run(params, pw); + out = s.toString(); + } + int errors = 0; + for (String eo: expectedOut) { + if (!out.contains(eo)) { + System.err.println("Match not found for string: " + eo); + errors++; + } + } + if (errors > 0) { + throw new AssertionError("Unexpected javap output: " + out); + } + } +} diff --git a/test/langtools/tools/javac/valhalla/lworld-values/UnifiedPrimitiveClassInnerClassesTest.java b/test/langtools/tools/javac/valhalla/lworld-values/UnifiedPrimitiveClassInnerClassesTest.java new file mode 100644 index 00000000000..30dff1a633a --- /dev/null +++ b/test/langtools/tools/javac/valhalla/lworld-values/UnifiedPrimitiveClassInnerClassesTest.java @@ -0,0 +1,74 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 8265423 + * @summary Experimental support for generating a single class file per primitive class + * @modules jdk.jdeps/com.sun.tools.classfile + * @compile -XDunifiedValRefClass UnifiedPrimitiveClassInnerClassesTest.java + * @run main UnifiedPrimitiveClassInnerClassesTest + */ + +import com.sun.tools.classfile.*; +import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info; + +public class UnifiedPrimitiveClassInnerClassesTest { + + primitive class V implements java.io.Serializable {} + + public static void main(String[] args) throws Exception { + ClassFile cls = ClassFile.read(UnifiedPrimitiveClassInnerClassesTest.class.getResourceAsStream("UnifiedPrimitiveClassInnerClassesTest.class")); + + if (cls == null) { + throw new AssertionError("Could not locate the class files"); + } + + /* Check emission of inner class attribute */ + InnerClasses_attribute inners = (InnerClasses_attribute) cls.attributes.get(Attribute.InnerClasses); + if (inners == null) { + throw new AssertionError("Missing inner class attribute"); + } + boolean foundV = false, foundVref = false; + for (int i = 0; i < inners.number_of_classes; i++) { + String name = inners.classes[i].getInnerName(cls.constant_pool); + if (name.equals("V")) + foundV = true; + else if (name.equals("V$ref")) + foundVref = true; + } + if (!foundV || foundVref) { + throw new AssertionError("Incorrect inner class attribute"); + } + + // Test signature attribute + cls = ClassFile.read(UnifiedPrimitiveClassInnerClassesTest.class.getResourceAsStream("UnifiedPrimitiveClassInnerClassesTest$V.class")); + Signature_attribute signature = (Signature_attribute)cls.attributes.get(Attribute.Signature); + String sign = signature.getSignature(cls.constant_pool); + if (sign == null || !sign.equals("Ljava/lang/Object;Ljava/io/Serializable;")) { + throw new RuntimeException("Wrong signature " + sign); + } + } +} diff --git a/test/langtools/tools/javac/valhalla/lworld-values/UnifiedPrimitiveClassNestHostTest.java b/test/langtools/tools/javac/valhalla/lworld-values/UnifiedPrimitiveClassNestHostTest.java new file mode 100644 index 00000000000..1becb6cc8a9 --- /dev/null +++ b/test/langtools/tools/javac/valhalla/lworld-values/UnifiedPrimitiveClassNestHostTest.java @@ -0,0 +1,78 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 8265423 + * @summary Experimental support for generating a single class file per primitive class + * @compile -XDunifiedValRefClass UnifiedPrimitiveClassNestHostTest.java + * @run main UnifiedPrimitiveClassNestHostTest + */ + +import java.util.Arrays; + +public primitive class UnifiedPrimitiveClassNestHostTest implements java.io.Serializable { + + primitive class Inner {} + + public static void main(String [] args) { + + // check wiring of super types. + Class superClass = UnifiedPrimitiveClassNestHostTest.class.getSuperclass(); + if (!superClass.equals(Object.class)) + throw new AssertionError("Wrong superclass for UnifiedPrimitiveClassNestHostTest"); + + Class [] superInterfaces = UnifiedPrimitiveClassNestHostTest.class.getInterfaces(); + if (superInterfaces.length != 2) + throw new AssertionError("Wrong super interfaces for UnifiedPrimitiveClassNestHostTest"); + + if (!superInterfaces[0].equals(java.io.Serializable.class)) + throw new AssertionError("Wrong super interfaces for UnifiedPrimitiveClassNestHostTest"); + if (!superInterfaces[1].equals(PrimitiveObject.class)) + throw new AssertionError("Wrong super interfaces for UnifiedPrimitiveClassNestHostTest"); + + Class nestHost = UnifiedPrimitiveClassNestHostTest.class.getNestHost(); + String hostName = nestHost.getName(); + + if (!hostName.equals("UnifiedPrimitiveClassNestHostTest")) + throw new AssertionError("Wrong nest host: " + hostName); + + Class [] members = nestHost.getNestMembers(); + if (members.length != 2) + throw new AssertionError("Wrong member count: " + members.length); + + if (!members[0].equals(nestHost)) + throw new AssertionError("Wrong initial member: " + members[0]); + + if (!members[1].equals(Inner.class)) + throw new AssertionError("Wrong initial member: " + members[1]); + + if (!members[1].getNestHost().equals(nestHost)) + throw new AssertionError("Wrong nest host for member[1]: " + members[1]); + + if (!Arrays.equals(members[1].getNestMembers(), members)) + throw new AssertionError("Wrong members for member[1]: " + members[1]); + } +}