< prev index next > src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
Print this page
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.code.Source.Feature;
+ import com.sun.tools.javac.code.Type.ClassType.Flavor;
import com.sun.tools.javac.comp.Annotate;
import com.sun.tools.javac.comp.Annotate.AnnotationTypeCompleter;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Directive.*;
import com.sun.tools.javac.code.Lint.LintCategory;
/** Switch: allow modules.
*/
boolean allowModules;
+ /** Switch: allow primitive classes.
+ */
+ boolean allowPrimitiveClasses;
+
+ /** Switch: allow value classes.
+ */
+ boolean allowValueClasses;
+
/** Switch: allow sealed
*/
boolean allowSealedTypes;
/** Switch: allow records
verbose = options.isSet(Option.VERBOSE);
Source source = Source.instance(context);
preview = Preview.instance(context);
allowModules = Feature.MODULES.allowedInSource(source);
+ allowPrimitiveClasses = Feature.PRIMITIVE_CLASSES.allowedInSource(source) && options.isSet("enablePrimitiveClasses");
+ allowValueClasses = Feature.VALUE_CLASSES.allowedInSource(source);
allowRecords = Feature.RECORDS.allowedInSource(source);
allowSealedTypes = Feature.SEALED_CLASSES.allowedInSource(source);
warnOnIllegalUtf8 = Feature.WARN_ON_ILLEGAL_UTF8.allowedInSource(source);
saveParameterNames = options.isSet(PARAMETERS);
sigp++;
return syms.intType;
case 'J':
sigp++;
return syms.longType;
+ case 'Q':
case 'L':
{
// int oldsigp = sigp;
+ if ((char) signature[sigp] == 'Q' && !allowPrimitiveClasses) {
+ throw badClassFile("bad.class.signature",
+ quoteBadSignature());
+ }
Type t = classSigToType();
if (sigp < siglimit && signature[sigp] == '.')
throw badClassFile("deprecated inner class signature syntax " +
"(please recompile from source)");
/*
byte[] signatureBuffer = new byte[0];
int sbp = 0;
/** Convert class signature to type, where signature is implicit.
*/
Type classSigToType() {
- if (signature[sigp] != 'L')
+ byte prefix = signature[sigp];
+ if (prefix != 'L' && (!allowPrimitiveClasses || prefix != 'Q'))
throw badClassFile("bad.class.signature", quoteBadSignature());
sigp++;
Type outer = Type.noType;
+ Name name;
+ ClassType.Flavor flavor;
int startSbp = sbp;
while (true) {
final byte c = signature[sigp++];
switch (c) {
case ';': { // end
ClassSymbol t = enterClass(readName(signatureBuffer,
startSbp,
sbp - startSbp));
+ // We are seeing QFoo; or LFoo; The name itself does not shine any light on default val-refness
+ flavor = prefix == 'L' ? Flavor.L_TypeOf_X : Flavor.Q_TypeOf_X;
try {
- return (outer == Type.noType) ?
- t.erasure(types) :
- new ClassType(outer, List.nil(), t);
+ if (outer == Type.noType) {
+ ClassType et = (ClassType) t.erasure(types);
+ // Todo: This spews out more objects than before, i.e no reuse with identical flavor
+ return new ClassType(et.getEnclosingType(), List.nil(), et.tsym, et.getMetadata(), flavor);
+ }
+ return new ClassType(outer, List.nil(), t, List.nil(), flavor);
} finally {
sbp = startSbp;
}
}
case '<': // generic arguments
ClassSymbol t = enterClass(readName(signatureBuffer,
startSbp,
sbp - startSbp));
- outer = new ClassType(outer, sigToTypes('>'), t) {
+ // We are seeing QFoo; or LFoo; The name itself does not shine any light on default val-refness
+ flavor = prefix == 'L' ? Flavor.L_TypeOf_X : Flavor.Q_TypeOf_X;
+ outer = new ClassType(outer, sigToTypes('>'), t, List.nil(), flavor) {
boolean completed = false;
@Override @DefinedBy(Api.LANGUAGE_MODEL)
public Type getEnclosingType() {
if (!completed) {
completed = true;
//we have seen an enclosing non-generic class
if (outer != Type.noType) {
t = enterClass(readName(signatureBuffer,
startSbp,
sbp - startSbp));
- outer = new ClassType(outer, List.nil(), t);
+ // We are seeing QFoo; or LFoo; The name itself does not shine any light on default val-refness
+ flavor = prefix == 'L' ? Flavor.L_TypeOf_X : Flavor.Q_TypeOf_X;
+ outer = new ClassType(outer, List.nil(), t, List.nil(), flavor);
}
signatureBuffer[sbp++] = (byte)'$';
continue;
case '/':
signatureBuffer[sbp++] = (byte)'.';
AttributeReader[] readers = {
// v45.3 attributes
new AttributeReader(names.Code, V45_3, MEMBER_ATTRIBUTE) {
protected void read(Symbol sym, int attrLen) {
+ if (sym.isInitOrVNew() && sym.type.getParameterTypes().size() == 0) {
+ try {
+ int code_length = buf.getInt(bp + 4);
+ if ((code_length == 1 && buf.getByte(bp + 8) == (byte) ByteCodes.return_) ||
+ (code_length == 5 && buf.getByte(bp + 8) == ByteCodes.aload_0 &&
+ buf.getByte(bp + 9) == (byte) ByteCodes.invokespecial &&
+ buf.getByte(bp + 12) == (byte) ByteCodes.return_)) {
+ sym.flags_field |= EMPTYNOARGCONSTR;
+ }
+ } catch (UnderflowException e) {
+ throw badClassFile("bad.class.truncated.at.offset", Integer.toString(e.getLength()));
+ }
+ }
if (saveParameterNames)
((MethodSymbol)sym).code = readCode(sym);
else
bp = bp + attrLen;
}
List<Type> thrown = sym.type.getThrownTypes();
sym.type = poolReader.getType(nextChar());
//- System.err.println(" # " + sym.type);
if (sym.kind == MTH && sym.type.getThrownTypes().isEmpty())
sym.type.asMethodType().thrown = thrown;
+ // Map value class factory methods back to constructors for the benefit of earlier pipeline stages
+ if (sym.kind == MTH && sym.name == names.vnew && !sym.type.getReturnType().hasTag(TypeTag.VOID)) {
+ sym.type = new MethodType(sym.type.getParameterTypes(),
+ syms.voidType,
+ sym.type.getThrownTypes(),
+ syms.methodClass);
+ }
}
}
},
for (Symbol sym : scope.getSymbolsByName(nt.name)) {
if (sym.kind == MTH && isSameBinaryType(sym.type.asMethodType(), type))
return (MethodSymbol)sym;
}
- if (nt.name != names.init)
+ if (!names.isInitOrVNew(nt.name))
// not a constructor
return null;
if ((flags & INTERFACE) != 0)
// no enclosing instance
return null;
throw badClassFile((flags & STATIC) == 0 ? "invalid.default.interface" : "invalid.static.interface",
Integer.toString(majorVersion),
Integer.toString(minorVersion));
}
}
+ if (names.isInitOrVNew(name) && ((flags & STATIC) != 0)) {
+ flags &= ~STATIC;
+ type = new MethodType(type.getParameterTypes(),
+ syms.voidType,
+ type.getThrownTypes(),
+ syms.methodClass);
+ }
validateMethodType(name, type);
- if (name == names.init && currentOwner.hasOuterInstance()) {
+ if (names.isInitOrVNew(name) && currentOwner.hasOuterInstance()) {
// Sometimes anonymous classes don't have an outer
// instance, however, there is no reliable way to tell so
// we never strip this$n
// ditto for local classes. Local classes that have an enclosing method set
// won't pass the "hasOuterInstance" check above, but those that don't have an
return m;
}
void validateMethodType(Name name, Type t) {
if ((!t.hasTag(TypeTag.METHOD) && !t.hasTag(TypeTag.FORALL)) ||
- (name == names.init && !t.getReturnType().hasTag(TypeTag.VOID))) {
+ ((name == names.init || name == names.vnew) && !t.getReturnType().hasTag(TypeTag.VOID))) {
throw badClassFile("method.descriptor.invalid", name);
}
}
private List<Type> adjustMethodParams(long flags, List<Type> args) {
// parameter when setting up the MethodType. If so, we
// make a corresponding allowance here for the position of
// the first parameter. Note that this assumes the
// skipped parameter has a width of 1 -- i.e. it is not
// a double width type (long or double.)
- if (sym.name == names.init && currentOwner.hasOuterInstance()) {
+ if (names.isInitOrVNew(sym.name) && currentOwner.hasOuterInstance()) {
// Sometimes anonymous classes don't have an outer
// instance, however, there is no reliable way to tell so
// we never strip this$n
if (!currentOwner.name.isEmpty())
firstParamLvt += 1;
enterTypevars(c.owner, ct.getEnclosingType());
// read flags, or skip if this is an inner class
long f = nextChar();
long flags = adjustClassFlags(f);
+ if (c == syms.objectType.tsym) {
+ flags &= ~IDENTITY_TYPE; // jlO lacks identity even while being a concrete class.
+ }
+ if ((flags & PRIMITIVE_CLASS) != 0) {
+ if (!allowPrimitiveClasses || (flags & (FINAL | PRIMITIVE_CLASS | IDENTITY_TYPE)) != (FINAL | PRIMITIVE_CLASS)) {
+ throw badClassFile("bad.access.flags", Flags.toString(flags));
+ }
+ }
if ((flags & MODULE) == 0) {
if (c.owner.kind == PCK || c.owner.kind == ERR) c.flags_field = flags;
// read own class name and check that it matches
currentModule = c.packge().modle;
ClassSymbol self = poolReader.getClass(nextChar());
}
return flags;
}
long adjustClassFlags(long flags) {
+ if ((flags & (ABSTRACT | INTERFACE | ACC_VALUE | ACC_MODULE)) == 0) {
+ flags |= ACC_IDENTITY;
+ }
if ((flags & ACC_MODULE) != 0) {
flags &= ~ACC_MODULE;
flags |= MODULE;
}
- return flags & ~ACC_SUPER; // SUPER and SYNCHRONIZED bits overloaded
+ if ((flags & ACC_PRIMITIVE) != 0) {
+ flags &= ~ACC_PRIMITIVE;
+ if (allowPrimitiveClasses) {
+ flags |= PRIMITIVE_CLASS;
+ }
+ }
+ if ((flags & ACC_VALUE) != 0) {
+ flags &= ~ACC_VALUE;
+ if (allowValueClasses) {
+ flags |= VALUE_CLASS;
+ }
+ }
+ if ((flags & ACC_IDENTITY) != 0) {
+ flags &= ~ACC_IDENTITY;
+ flags |= IDENTITY_TYPE;
+ }
+ return flags;
}
/**
* A subclass of JavaFileObject for the sourcefile attribute found in a classfile.
* The attribute is only the last component of the original filename, so is unlikely
< prev index next >