Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8267597: [lworld] Withdraw all support for bifurcated class generation for primitive classes #441

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -420,21 +420,6 @@ public boolean isPrimitiveClass() {
return (flags() & PRIMITIVE_CLASS) != 0;
}

/**
* Is this a *derived* reference projection symbol ??
*/
public boolean isReferenceProjection() {
return false;
}

/**
* If this is the symbol for a reference projection class, what is the class for which
* this is a projection ??
*/
public ClassSymbol valueProjection() {
return null;
}

public boolean isPublic() {
return (flags_field & Flags.AccessFlags) == PUBLIC;
}
@@ -545,8 +530,6 @@ public ClassSymbol enclClass() {
}

/** The outermost class which indirectly owns this symbol.
* 'outermost' being a lexical construct, should transcend
* projections
*/
public ClassSymbol outermostClass() {
Symbol sym = this;
@@ -555,7 +538,7 @@ public ClassSymbol outermostClass() {
prev = sym;
sym = sym.owner;
}
return (ClassSymbol) (prev!= null && prev.isReferenceProjection() ? prev.valueProjection() : prev);
return (ClassSymbol) prev;
}

/** The package which indirectly owns this symbol.
@@ -1431,14 +1414,6 @@ 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 {
@@ -102,18 +102,6 @@
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">
@@ -139,7 +127,6 @@ protected Types(Context context) {
noWarnings = new Warner(null);
Options options = Options.instance(context);
allowValueBasedClasses = options.isSet("allowValueBasedClasses");
splitPrimitiveClass = false ; // options.isUnset("unifiedValRefClass"); // Temporarely forcing the default
}
// </editor-fold>

@@ -1880,7 +1867,7 @@ public Boolean visitClassType(ClassType t, Type s) {
// Sidecast
if (s.hasTag(CLASS)) {
if ((s.tsym.flags() & INTERFACE) != 0) {
return (dynamicTypeMayImplementAdditionalInterfaces(t.tsym))
return ((t.tsym.flags() & FINAL) == 0)
? sideCast(t, s, warnStack.head)
: sideCastFinal(t, s, warnStack.head);
} else if ((t.tsym.flags() & INTERFACE) != 0) {
@@ -4672,7 +4659,7 @@ private boolean sideCastFinal(Type from, Type to, Warner warn) {
to = from;
from = target;
}
Assert.check(!dynamicTypeMayImplementAdditionalInterfaces(from.tsym));
Assert.check((from.tsym.flags() & FINAL) != 0);
Type t1 = asSuper(from, to.tsym);
if (t1 == null) return false;
Type t2 = to;
@@ -4684,10 +4671,6 @@ private boolean sideCastFinal(Type from, Type to, Warner warn) {
return true;
}

private boolean dynamicTypeMayImplementAdditionalInterfaces(TypeSymbol tsym) {
return (tsym.flags() & FINAL) == 0 && !tsym.isReferenceProjection();
}

private boolean giveWarning(Type from, Type to) {
List<Type> bounds = to.isCompound() ?
directSupertypes(to) : List.of(to);
@@ -5366,10 +5349,6 @@ public void assembleClassSig(Type type) {
} else {
append(externalize(c.flatname));
}
if (types.splitPrimitiveClass && ct.isReferenceProjection()) {
append('$');
append(types.names.ref);
}
if (ct.getTypeArguments().nonEmpty()) {
append('<');
assembleSig(ct.getTypeArguments());
@@ -3106,12 +3106,6 @@ public void visitApply(JCMethodInvocation tree) {
boolean haveValue = tree.type.isPrimitiveClass();
if (haveValue == type.isPrimitiveClass())
return tree;
if (haveValue) {
// 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);
@@ -822,13 +822,11 @@ int writeModuleAttribute(ClassSymbol c) {
* Writing Objects
**********************************************************************/

/** Write "inner classes" attribute. If a primitive class happens to be an inner class,
* the reference projection class will also be an inner class.
/** Write "inner classes" attribute.
*/
void writeInnerClasses() {
int alenIdx = writeAttr(names.InnerClasses);
int icCountIdx = beginAttrs();
int icCount = 0;
databuf.appendChar(poolWriter.innerClasses.size());
for (ClassSymbol inner : poolWriter.innerClasses) {
inner.markAbstractIfNeeded(types);
char flags = (char) adjustFlags(inner.flags_field);
@@ -845,19 +843,7 @@ void writeInnerClasses() {
databuf.appendChar(
!inner.name.isEmpty() ? poolWriter.putName(inner.name) : 0);
databuf.appendChar(flags);
icCount++;
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);
databuf.appendChar(
!inner.name.isEmpty() ? poolWriter.putName(inner.name.append('$', names.ref)) : 0);
flags = (char) ((flags & ~(ACC_PRIMITIVE | FINAL)) | ABSTRACT);
databuf.appendChar(flags);
icCount++;
}
}
endAttrs(icCountIdx, icCount);
endAttr(alenIdx);
}

@@ -882,33 +868,17 @@ int writeRecordAttribute(ClassSymbol csym) {
* Write NestMembers attribute (if needed)
*/
int writeNestMembersIfNeeded(ClassSymbol csym) {
Set<ClassSymbol> nestedUnique = new LinkedHashSet<>();
if (csym.owner.kind == PCK) {
if (csym.isSplitPrimitiveClass(types)) {
// reference projection is the host
} else if (csym.isReferenceProjection()) {
ClassSymbol valueProjection = csym.valueProjection();
nestedUnique.add(valueProjection);
listNested(valueProjection, nestedUnique);
} else {
listNested(csym, nestedUnique);
}
if (!nestedUnique.isEmpty()) {
int alenIdx = writeAttr(names.NestMembers);
int nmcIdx = beginAttrs();
int nmc = 0;
for (ClassSymbol s : nestedUnique) {
databuf.appendChar(poolWriter.putClass(s));
nmc++;
if (s.isSplitPrimitiveClass(types) && s.owner.kind != PCK) {
databuf.appendChar(poolWriter.putClass(s.type.referenceProjection()));
nmc++;
}
}
endAttrs(nmcIdx, nmc);
endAttr(alenIdx);
return 1;
ListBuffer<ClassSymbol> nested = new ListBuffer<>();
listNested(csym, nested);
Set<ClassSymbol> nestedUnique = new LinkedHashSet<>(nested);
if (csym.owner.kind == PCK && !nestedUnique.isEmpty()) {
int alenIdx = writeAttr(names.NestMembers);
databuf.appendChar(nestedUnique.size());
for (ClassSymbol s : nestedUnique) {
databuf.appendChar(poolWriter.putClass(s));
}
endAttr(alenIdx);
return 1;
}
return 0;
}
@@ -917,21 +887,16 @@ int writeNestMembersIfNeeded(ClassSymbol csym) {
* Write NestHost attribute (if needed)
*/
int writeNestHostIfNeeded(ClassSymbol csym) {
if (csym.owner.kind != PCK || csym.isSplitPrimitiveClass(types)) {
if (csym.owner.kind != PCK) {
int alenIdx = writeAttr(names.NestHost);
ClassSymbol outerMost = csym.outermostClass();
if (outerMost.isSplitPrimitiveClass(types)) {
databuf.appendChar(poolWriter.putClass(outerMost.type.referenceProjection()));
} else {
databuf.appendChar(poolWriter.putClass(outerMost));
}
databuf.appendChar(poolWriter.putClass(csym.outermostClass()));
endAttr(alenIdx);
return 1;
}
return 0;
}

private void listNested(Symbol sym, Set<ClassSymbol> seen) {
private void listNested(Symbol sym, ListBuffer<ClassSymbol> seen) {
if (sym.kind != TYP) return;
ClassSymbol csym = (ClassSymbol)sym;
if (csym.owner.kind != PCK) {
@@ -1531,67 +1496,9 @@ public JavaFileObject writeClass(ClassSymbol c)
throws IOException, PoolOverflow, StringOverflow
{
JavaFileObject javaFileObject = writeClassInternal(c);
if (c.isSplitPrimitiveClass(types)) {
writeClassInternal(getReferenceProjection(c));
}
return javaFileObject;
}

// where
private static ClassSymbol getReferenceProjection(ClassSymbol c) {

ClassSymbol projection;
ClassType projectedType;

ClassType ct = (ClassType) c.type;
/* Note, the class type associated with the Primitive$ref.class is NOT a reference projection type. A reference projection
* type gets created by using Primitive.ref notation in the source file or while reading in a descriptor of such a type
* from the class file. Here we are generating the Primitive$ref.class for the VM's benefit and it is a reference class.
*/
projectedType = new ClassType(ct.getEnclosingType(), ct.typarams_field, null, ct.getMetadata(), Flavor.L_TypeOf_L);
projectedType.allparams_field = ct.allparams_field;
projectedType.supertype_field = ct.supertype_field;

projectedType.interfaces_field = ct.interfaces_field;
projectedType.all_interfaces_field = ct.all_interfaces_field;
projectedType.projection = null;

Name projectionName = c.name.append('$', c.name.table.names.ref);
long projectionFlags = (c.flags() & ~(PRIMITIVE_CLASS | UNATTRIBUTED | FINAL)) | (ABSTRACT | SEALED);

projection = new ClassSymbol(projectionFlags, projectionName, projectedType, c.owner) {
@Override
public boolean isReferenceProjection() {
return true;
}

@Override
public ClassSymbol valueProjection() {
return c;
}
};
projection.members_field = WriteableScope.create(projection);
for (Symbol s : c.members().getSymbols(s->(s.kind == MTH || s.kind == VAR), NON_RECURSIVE)) {
Symbol clone = null;
if (s.kind == MTH) {
MethodSymbol valMethod = (MethodSymbol)s;
MethodSymbol refMethod = valMethod.clone(projection);
clone = refMethod;
} else if (s.kind == VAR) {
VarSymbol valVar = (VarSymbol)s;
VarSymbol refVar = valVar.clone(projection);
clone = refVar;
}
projection.members_field.enter(clone);
}
projection.completer = Completer.NULL_COMPLETER;
projection.sourcefile = c.sourcefile;
projection.flatname = c.flatname.append('$', c.name.table.names.ref);
projection.permitted = List.of(c);
projectedType.tsym = projection;
return projection;
}

private JavaFileObject writeClassInternal(ClassSymbol c)
throws IOException, PoolOverflow, StringOverflow
{
@@ -1636,8 +1543,8 @@ public void writeClassFile(OutputStream out, ClassSymbol c)
databuf.reset();
poolbuf.reset();

Type supertype = c.isSplitPrimitiveClass(types) ? c.type.referenceProjection() : types.supertype(c.type);
List<Type> interfaces = c.isSplitPrimitiveClass(types) ? List.nil() : types.interfaces(c.type);
Type supertype = types.supertype(c.type);
List<Type> interfaces = types.interfaces(c.type);
List<Type> typarams = c.type.getTypeArguments();

int flags;
@@ -1670,37 +1577,33 @@ public void writeClassFile(OutputStream out, ClassSymbol c)
databuf.appendChar(poolWriter.putClass((ClassSymbol)l.head.tsym));
int fieldsCount = 0;
int methodsCount = 0;
boolean referenceProjection = c.isReferenceProjection();
if (!referenceProjection) {
for (Symbol sym : c.members().getSymbols(NON_RECURSIVE)) {
switch (sym.kind) {
case VAR:
fieldsCount++;
break;
case MTH:
if ((sym.flags() & HYPOTHETICAL) == 0) methodsCount++;
break;
case TYP:
poolWriter.enterInner((ClassSymbol)sym);
break;
default:
Assert.error();
}

for (Symbol sym : c.members().getSymbols(NON_RECURSIVE)) {
switch (sym.kind) {
case VAR:
fieldsCount++;
break;
case MTH:
if ((sym.flags() & HYPOTHETICAL) == 0) methodsCount++;
break;
case TYP:
poolWriter.enterInner((ClassSymbol)sym);
break;
default:
Assert.error();
}
}

if (c.trans_local != null) {
for (ClassSymbol local : c.trans_local) {
poolWriter.enterInner(local);
}
if (c.trans_local != null) {
for (ClassSymbol local : c.trans_local) {
poolWriter.enterInner(local);
}
}

databuf.appendChar(fieldsCount);
if (!referenceProjection)
writeFields(c.members());
writeFields(c.members());
databuf.appendChar(methodsCount);
if (!referenceProjection)
writeMethods(c.members());
writeMethods(c.members());

int acountIdx = beginAttrs();
int acount = 0;
@@ -2271,10 +2271,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 when we bifurcate the primitive class, as the VM sees a subtyping relationship.
// inline widening conversion should not require a checkcast but we issue one per VM's request as of now (see || true below)
if (!tree.clazz.type.isPrimitive() &&
!types.isSameType(tree.expr.type, tree.clazz.type) &&
(!tree.clazz.type.isReferenceProjection() || !types.splitPrimitiveClass || !types.isSameType(tree.clazz.type.valueProjection(), tree.expr.type)) &&
(!tree.clazz.type.isReferenceProjection() || !types.isSameType(tree.clazz.type.valueProjection(), tree.expr.type) || true) &&
!types.isSubtype(tree.expr.type, tree.clazz.type)) {
checkDimension(tree.pos(), tree.clazz.type);
if (types.isPrimitiveClass(tree.clazz.type)) {
@@ -2344,7 +2344,7 @@ public void visitSelect(JCFieldAccess tree) {
Symbol sym = tree.sym;

if (tree.name == names._class) {
code.emitLdc((LoadableConstant) tree.selected.type, makeRef(tree.pos(), tree.selected.type, !types.splitPrimitiveClass && tree.selected.type.isPrimitiveClass()));
code.emitLdc((LoadableConstant) tree.selected.type, makeRef(tree.pos(), tree.selected.type, tree.selected.type.isPrimitiveClass()));
result = items.makeStackItem(pt);
return;
}