< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java

Print this page
@@ -43,10 +43,11 @@
  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;

@@ -106,10 +107,18 @@
  
      /** 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

@@ -280,10 +289,12 @@
          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);

@@ -498,13 +509,18 @@
              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)");
                  /*

@@ -558,14 +574,17 @@
      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) {

@@ -573,24 +592,31 @@
              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;

@@ -649,11 +675,13 @@
                  //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)'.';

@@ -841,10 +869,23 @@
          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;
                  }

@@ -1020,10 +1061,17 @@
                          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);
+                         }
  
                      }
                  }
              },
  

@@ -1373,11 +1421,11 @@
          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;

@@ -2301,12 +2349,19 @@
                  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

@@ -2347,11 +2402,11 @@
          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) {

@@ -2411,11 +2466,11 @@
          // 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;

@@ -2575,10 +2630,18 @@
              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());

@@ -2832,15 +2895,34 @@
          }
          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 >