Skip to content
Permalink
Browse files
8265423: [lworld] Provide experimental support for generating a singl…
…e class file per primitive class
  • Loading branch information
Srikanth Adayapalam committed May 3, 2021
1 parent a7ab3bb commit 17b12ac7e3169beff5fb7b0caa561d391a94c455
@@ -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 {
@@ -101,6 +101,18 @@ public class Types {
List<Warner> 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;

// <editor-fold defaultstate="collapsed" desc="Instantiating">
@@ -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");
}
// </editor-fold>

@@ -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);
}
@@ -3108,12 +3108,15 @@ <T extends JCExpression> 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);

}


@@ -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;
@@ -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<ClassSymbol> 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<Type> interfaces = c.isPrimitiveClass() ? List.nil() : types.interfaces(c.type);
Type supertype = c.isSplitPrimitiveClass(types) ? c.type.referenceProjection() : types.supertype(c.type);
List<Type> interfaces = c.isSplitPrimitiveClass(types) ? List.nil() : types.interfaces(c.type);
List<Type> typarams = c.type.getTypeArguments();

int flags;
@@ -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) {
@@ -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;
}
@@ -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);
}
@@ -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);
}
}
}
@@ -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<T> 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("<T:Ljava/lang/Object;>LSplitPrimitiveClassInnerClassesTest$V$ref<TT;>;")) {
throw new RuntimeException("Wrong signature " + sign);
}
}
}

0 comments on commit 17b12ac

Please sign in to comment.