8152911: javac assertion error when compiling overlay sources
authorjlahoda
Tue, 04 Oct 2016 16:25:19 +0200
changeset 41254 08f8dbf7741e
parent 41253 7a2422a5b481
child 41255 72fcbd6294cb
8152911: javac assertion error when compiling overlay sources Summary: Avoid creating ModuleSymbols with unspecified name, to avoid conflicts with predefined ModuleSymbol for the java.base module. Reviewed-by: jjg
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ModuleFinder.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/ModuleNameReader.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ModuleNameReader.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
langtools/test/tools/javac/diags/examples.not-yet.txt
langtools/test/tools/javac/modules/EdgeCases.java
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ModuleFinder.java	Tue Oct 04 13:41:52 2016 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ModuleFinder.java	Tue Oct 04 16:25:19 2016 +0200
@@ -31,6 +31,7 @@
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.function.Function;
 
 import javax.tools.JavaFileManager;
 import javax.tools.JavaFileManager.Location;
@@ -38,10 +39,14 @@
 import javax.tools.JavaFileObject.Kind;
 import javax.tools.StandardLocation;
 
+import com.sun.tools.javac.code.Symbol.Completer;
 import com.sun.tools.javac.code.Symbol.CompletionFailure;
 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
+import com.sun.tools.javac.jvm.ModuleNameReader;
+import com.sun.tools.javac.jvm.ModuleNameReader.BadClassFile;
 import com.sun.tools.javac.resources.CompilerProperties.Errors;
 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
+import com.sun.tools.javac.util.Assert;
 import com.sun.tools.javac.util.Context;
 import com.sun.tools.javac.util.JCDiagnostic;
 import com.sun.tools.javac.util.JCDiagnostic.Fragment;
@@ -50,7 +55,6 @@
 import com.sun.tools.javac.util.Log;
 import com.sun.tools.javac.util.Name;
 import com.sun.tools.javac.util.Names;
-import com.sun.tools.javac.util.StringUtils;
 
 import static com.sun.tools.javac.code.Kinds.Kind.*;
 
@@ -84,6 +88,10 @@
 
     private final JCDiagnostic.Factory diags;
 
+    private ModuleNameReader moduleNameReader;
+
+    public ModuleInfoSourceFileCompleter sourceFileCompleter;
+
     /** Get the ModuleFinder instance for this invocation. */
     public static ModuleFinder instance(Context context) {
         ModuleFinder instance = context.get(moduleFinderKey);
@@ -182,6 +190,8 @@
         return list;
     }
 
+    private boolean inFindSingleModule;
+
     public ModuleSymbol findSingleModule() {
         try {
             JavaFileObject src_fo = getModuleInfoFromLocation(StandardLocation.SOURCE_PATH, Kind.SOURCE);
@@ -194,26 +204,41 @@
             if (fo == null) {
                 msym = syms.unnamedModule;
             } else {
-                // Note: the following may trigger a re-entrant call to Modules.enter
-//                msym = new ModuleSymbol();
-//                ClassSymbol info = new ClassSymbol(Flags.MODULE, names.module_info, msym);
-//                info.modle = msym;
-//                info.classfile = fo;
-//                info.members_field = WriteableScope.create(info);
-//                msym.module_info = info;
-                msym = ModuleSymbol.create(null, names.module_info);
-                msym.module_info.classfile = fo;
-                msym.completer = sym -> classFinder.fillIn(msym.module_info);
-//                // TODO: should we do the following here, or as soon as we find the name in
-//                // the source or class file?
-//                // Consider the case when the class/source path module shadows one on the
-//                // module source path
-//                if (syms.modules.get(msym.name) != null) {
-//                    // error: module already defined
-//                    System.err.println("ERROR: module already defined: " + msym);
-//                } else {
-//                    syms.modules.put(msym.name, msym);
-//                }
+                switch (fo.getKind()) {
+                    case SOURCE:
+                        if (!inFindSingleModule) {
+                            try {
+                                inFindSingleModule = true;
+                                // Note: the following will trigger a re-entrant call to Modules.enter
+                                msym = sourceFileCompleter.complete(fo);
+                                msym.module_info.classfile = fo;
+                            } finally {
+                                inFindSingleModule = false;
+                            }
+                        } else {
+                            //the module-info.java does not contain a module declaration,
+                            //avoid infinite recursion:
+                            msym = syms.unnamedModule;
+                        }
+                        break;
+                    case CLASS:
+                        Name name;
+                        try {
+                            name = names.fromString(readModuleName(fo));
+                        } catch (BadClassFile | IOException ex) {
+                            //fillIn will report proper errors:
+                            name = names.error;
+                        }
+                        msym = syms.enterModule(name);
+                        msym.module_info.classfile = fo;
+                        msym.completer = Completer.NULL_COMPLETER;
+                        classFinder.fillIn(msym.module_info);
+                        break;
+                    default:
+                        Assert.error();
+                        msym = syms.unnamedModule;
+                        break;
+                }
             }
 
             msym.classLocation = StandardLocation.CLASS_OUTPUT;
@@ -224,6 +249,12 @@
         }
     }
 
+    private String readModuleName(JavaFileObject jfo) throws IOException, ModuleNameReader.BadClassFile {
+        if (moduleNameReader == null)
+            moduleNameReader = new ModuleNameReader();
+        return moduleNameReader.readModuleName(jfo);
+    }
+
     private JavaFileObject getModuleInfoFromLocation(Location location, Kind kind) throws IOException {
         if (!fileManager.hasLocation(location))
             return null;
@@ -332,4 +363,8 @@
         }
     }
 
+    public interface ModuleInfoSourceFileCompleter {
+        public ModuleSymbol complete(JavaFileObject file);
+    }
+
 }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Tue Oct 04 13:41:52 2016 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Tue Oct 04 16:25:19 2016 +0200
@@ -916,7 +916,6 @@
 
         /**
          * Create a ModuleSymbol with an associated module-info ClassSymbol.
-         * The name of the module may be null, if it is not known yet.
          */
         public static ModuleSymbol create(Name name, Name module_info) {
             ModuleSymbol msym = new ModuleSymbol(name, null);
@@ -930,6 +929,7 @@
 
         public ModuleSymbol(Name name, Symbol owner) {
             super(MDL, 0, name, null, owner);
+            Assert.checkNonNull(name);
             this.type = new ModuleType(this);
         }
 
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Tue Oct 04 13:41:52 2016 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Tue Oct 04 16:25:19 2016 +0200
@@ -764,17 +764,6 @@
         return msym;
     }
 
-    public void enterModule(ModuleSymbol msym, Name name) {
-        Assert.checkNull(modules.get(name));
-        Assert.checkNull(msym.name);
-        msym.name = name;
-        addRootPackageFor(msym);
-        ClassSymbol info = msym.module_info;
-        info.fullname = msym.name.append('.', names.module_info);
-        info.flatname = info.fullname;
-        modules.put(name, msym);
-    }
-
     public ModuleSymbol getModule(Name name) {
         return modules.get(name);
     }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java	Tue Oct 04 13:41:52 2016 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java	Tue Oct 04 16:25:19 2016 +0200
@@ -292,15 +292,12 @@
             Name name = TreeInfo.fullName(decl.qualId);
             ModuleSymbol sym;
             if (c != null) {
-               sym = (ModuleSymbol) c.owner;
-               if (sym.name == null) {
-                   //ModuleFinder.findSingleModule creates a stub of a ModuleSymbol without a name,
-                   //fill the name here after the module-info.java has been parsed
-                   //also enter the ModuleSymbol among modules:
-                   syms.enterModule(sym, name);
-               } else {
-                   // TODO: validate name
-               }
+                sym = (ModuleSymbol) c.owner;
+                Assert.checkNonNull(sym.name);
+                Name treeName = TreeInfo.fullName(decl.qualId);
+                if (sym.name != treeName) {
+                    log.error(decl.pos(), Errors.ModuleNameMismatch(name, sym.name));
+                }
             } else {
                 sym = syms.enterModule(name);
                 if (sym.module_info.sourcefile != null && sym.module_info.sourcefile != toplevel.sourcefile) {
@@ -1006,6 +1003,10 @@
         return new Symbol.Completer() {
             @Override
             public void complete(Symbol sym) throws CompletionFailure {
+                if (inInitModules) {
+                    sym.completer = this;
+                    return ;
+                }
                 ModuleSymbol msym = (ModuleSymbol) sym;
                 Set<ModuleSymbol> allModules = allModules();
                 for (ModuleSymbol m : allModules) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java	Tue Oct 04 13:41:52 2016 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java	Tue Oct 04 16:25:19 2016 +0200
@@ -80,6 +80,7 @@
 import com.sun.tools.javac.util.DefinedBy.Api;
 import com.sun.tools.javac.util.ListBuffer;
 import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.jvm.ModuleNameReader;
 import com.sun.tools.javac.util.Pair;
 import com.sun.tools.javac.util.StringUtils;
 
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/ModuleNameReader.java	Tue Oct 04 13:41:52 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,228 +0,0 @@
-/*
- * Copyright (c) 1999, 2016, 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.
- */
-package com.sun.tools.javac.file;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import com.sun.tools.javac.jvm.ClassFile;
-
-import static com.sun.tools.javac.jvm.ClassFile.*;
-
-
-/**
- * Stripped down ClassReader, just sufficient to read module names from module-info.class files
- * while analyzing the module path.
- *
- * <p>
- * <b>This is NOT part of any supported API. If you write code that depends on this, you do so at
- * your own risk. This code and its internal interfaces are subject to change or deletion without
- * notice.</b>
- */
-public class ModuleNameReader {
-    static class BadClassFile extends Exception {
-        private static final long serialVersionUID = 0;
-        BadClassFile(String msg) {
-            super(msg);
-        }
-    }
-
-    private static final int INITIAL_BUFFER_SIZE = 0x0fff0;
-
-    /** The buffer containing the currently read class file.
-     */
-    private byte[] buf = new byte[INITIAL_BUFFER_SIZE];
-
-    /** The current input pointer.
-     */
-    private int bp;
-
-    /** The objects of the constant pool.
-     */
-    private Object[] poolObj;
-
-    /** For every constant pool entry, an index into buf where the
-     *  defining section of the entry is found.
-     */
-    private int[] poolIdx;
-
-    ModuleNameReader() {
-    }
-
-    String readModuleName(Path p) throws IOException, BadClassFile {
-        try (InputStream in = Files.newInputStream(p)) {
-            bp = 0;
-            buf = readInputStream(buf, in);
-
-            int magic = nextInt();
-            if (magic != JAVA_MAGIC)
-                throw new BadClassFile("illegal.start.of.class.file");
-
-            int minorVersion = nextChar();
-            int majorVersion = nextChar();
-
-            indexPool();
-
-            int accessflags = nextChar();
-            return readModuleInfoName(nextChar());
-        }
-    }
-
-    /** Extract a character at position bp from buf.
-     */
-    char getChar(int bp) {
-        return
-            (char)(((buf[bp] & 0xFF) << 8) + (buf[bp+1] & 0xFF));
-    }
-
-    /** Read a character.
-     */
-    char nextChar() {
-        return (char)(((buf[bp++] & 0xFF) << 8) + (buf[bp++] & 0xFF));
-    }
-
-    /** Read an integer.
-     */
-    int nextInt() {
-        return
-            ((buf[bp++] & 0xFF) << 24) +
-            ((buf[bp++] & 0xFF) << 16) +
-            ((buf[bp++] & 0xFF) << 8) +
-            (buf[bp++] & 0xFF);
-    }
-
-    /** Index all constant pool entries, writing their start addresses into
-     *  poolIdx.
-     */
-    void indexPool() throws BadClassFile {
-        poolIdx = new int[nextChar()];
-        poolObj = new Object[poolIdx.length];
-        int i = 1;
-        while (i < poolIdx.length) {
-            poolIdx[i++] = bp;
-            byte tag = buf[bp++];
-            switch (tag) {
-            case CONSTANT_Utf8: case CONSTANT_Unicode: {
-                int len = nextChar();
-                bp = bp + len;
-                break;
-            }
-            case CONSTANT_Class:
-            case CONSTANT_String:
-            case CONSTANT_MethodType:
-                bp = bp + 2;
-                break;
-            case CONSTANT_MethodHandle:
-                bp = bp + 3;
-                break;
-            case CONSTANT_Fieldref:
-            case CONSTANT_Methodref:
-            case CONSTANT_InterfaceMethodref:
-            case CONSTANT_NameandType:
-            case CONSTANT_Integer:
-            case CONSTANT_Float:
-            case CONSTANT_InvokeDynamic:
-                bp = bp + 4;
-                break;
-            case CONSTANT_Long:
-            case CONSTANT_Double:
-                bp = bp + 8;
-                i++;
-                break;
-            default:
-                throw new BadClassFile("malformed constant pool");
-            }
-        }
-    }
-
-    /** Read the class name of a module-info.class file.
-     * The name is stored in a CONSTANT_Class entry, where the
-     * class name is of the form module-name/module-info.
-     */
-    String readModuleInfoName(int i) throws BadClassFile {
-        int classIndex = poolIdx[i];
-        if (buf[classIndex] == CONSTANT_Class) {
-            int utf8Index = poolIdx[getChar(classIndex + 1)];
-            if (buf[utf8Index] == CONSTANT_Utf8) {
-                int len = getChar(utf8Index + 1);
-                int start = utf8Index + 3;
-                String suffix = "/module-info";
-                if (endsWith(buf, start, len, suffix))
-                    return new String(ClassFile.internalize(buf, start, len - suffix.length()));
-            }
-        }
-        throw new BadClassFile("bad module-info name");
-    }
-
-    private boolean endsWith(byte[] buf, int start, int len, String suffix) {
-        if (len <= suffix.length())
-            return false;
-        for (int i = 0; i < suffix.length(); i++) {
-            if (buf[start + len - suffix.length() + i] != suffix.charAt(i))
-                return false;
-        }
-        return true;
-    }
-
-    private static byte[] readInputStream(byte[] buf, InputStream s) throws IOException {
-        try {
-            buf = ensureCapacity(buf, s.available());
-            int r = s.read(buf);
-            int bp = 0;
-            while (r != -1) {
-                bp += r;
-                buf = ensureCapacity(buf, bp);
-                r = s.read(buf, bp, buf.length - bp);
-            }
-            return buf;
-        } finally {
-            try {
-                s.close();
-            } catch (IOException e) {
-                /* Ignore any errors, as this stream may have already
-                 * thrown a related exception which is the one that
-                 * should be reported.
-                 */
-            }
-        }
-    }
-
-    /*
-     * ensureCapacity will increase the buffer as needed, taking note that
-     * the new buffer will always be greater than the needed and never
-     * exactly equal to the needed size or bp. If equal then the read (above)
-     * will infinitely loop as buf.length - bp == 0.
-     */
-    private static byte[] ensureCapacity(byte[] buf, int needed) {
-        if (buf.length <= needed) {
-            byte[] old = buf;
-            buf = new byte[Integer.highestOneBit(needed) << 1];
-            System.arraycopy(old, 0, buf, 0, old.length);
-        }
-        return buf;
-    }
-}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Tue Oct 04 13:41:52 2016 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Tue Oct 04 16:25:19 2016 +0200
@@ -2396,12 +2396,15 @@
         } else {
             c.flags_field = flags;
             Name modInfoName = readModuleInfoName(nextChar());
-            if (c.owner.name == null) {
-                syms.enterModule((ModuleSymbol) c.owner, Convert.packagePart(modInfoName));
-            } else {
-                // TODO: validate name
+            currentModule = (ModuleSymbol) c.owner;
+            if (currentModule.name.append('.', names.module_info) != modInfoName) {
+                //strip trailing .module-info, if exists:
+                int modInfoStart = modInfoName.length() - names.module_info.length();
+                modInfoName = modInfoName.subName(modInfoStart, modInfoName.length()) == names.module_info &&
+                              modInfoName.charAt(modInfoStart - 1) == '.' ?
+                                  modInfoName.subName(0, modInfoStart - 1) : modInfoName;
+                throw badClassFile("module.name.mismatch", modInfoName, currentModule.name);
             }
-            currentModule = (ModuleSymbol) c.owner;
         }
 
         // class attributes must be read before class
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ModuleNameReader.java	Tue Oct 04 16:25:19 2016 +0200
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1999, 2016, 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.
+ */
+package com.sun.tools.javac.jvm;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import javax.tools.JavaFileObject;
+
+import com.sun.tools.javac.jvm.ClassFile;
+
+import static com.sun.tools.javac.jvm.ClassFile.*;
+
+
+/**
+ * Stripped down ClassReader, just sufficient to read module names from module-info.class files
+ * while analyzing the module path.
+ *
+ * <p>
+ * <b>This is NOT part of any supported API. If you write code that depends on this, you do so at
+ * your own risk. This code and its internal interfaces are subject to change or deletion without
+ * notice.</b>
+ */
+public class ModuleNameReader {
+    public static class BadClassFile extends Exception {
+        private static final long serialVersionUID = 0;
+        BadClassFile(String msg) {
+            super(msg);
+        }
+    }
+
+    private static final int INITIAL_BUFFER_SIZE = 0x0fff0;
+
+    /** The buffer containing the currently read class file.
+     */
+    private byte[] buf = new byte[INITIAL_BUFFER_SIZE];
+
+    /** The current input pointer.
+     */
+    private int bp;
+
+    /** For every constant pool entry, an index into buf where the
+     *  defining section of the entry is found.
+     */
+    private int[] poolIdx;
+
+    public ModuleNameReader() {
+    }
+
+    public String readModuleName(Path p) throws IOException, BadClassFile {
+        try (InputStream in = Files.newInputStream(p)) {
+            return readModuleName(in);
+        }
+    }
+
+    public String readModuleName(JavaFileObject jfo) throws IOException, BadClassFile {
+        try (InputStream in = jfo.openInputStream()) {
+            return readModuleName(in);
+        }
+    }
+
+    public String readModuleName(InputStream in) throws IOException, BadClassFile {
+        bp = 0;
+        buf = readInputStream(buf, in);
+
+        int magic = nextInt();
+        if (magic != JAVA_MAGIC)
+            throw new BadClassFile("illegal.start.of.class.file");
+
+        int minorVersion = nextChar();
+        int majorVersion = nextChar();
+
+        indexPool();
+
+        int accessflags = nextChar();
+        return readModuleInfoName(nextChar());
+    }
+
+    /** Extract a character at position bp from buf.
+     */
+    char getChar(int bp) {
+        return
+            (char)(((buf[bp] & 0xFF) << 8) + (buf[bp+1] & 0xFF));
+    }
+
+    /** Read a character.
+     */
+    char nextChar() {
+        return (char)(((buf[bp++] & 0xFF) << 8) + (buf[bp++] & 0xFF));
+    }
+
+    /** Read an integer.
+     */
+    int nextInt() {
+        return
+            ((buf[bp++] & 0xFF) << 24) +
+            ((buf[bp++] & 0xFF) << 16) +
+            ((buf[bp++] & 0xFF) << 8) +
+            (buf[bp++] & 0xFF);
+    }
+
+    /** Index all constant pool entries, writing their start addresses into
+     *  poolIdx.
+     */
+    void indexPool() throws BadClassFile {
+        poolIdx = new int[nextChar()];
+        int i = 1;
+        while (i < poolIdx.length) {
+            poolIdx[i++] = bp;
+            byte tag = buf[bp++];
+            switch (tag) {
+            case CONSTANT_Utf8: case CONSTANT_Unicode: {
+                int len = nextChar();
+                bp = bp + len;
+                break;
+            }
+            case CONSTANT_Class:
+            case CONSTANT_String:
+            case CONSTANT_MethodType:
+                bp = bp + 2;
+                break;
+            case CONSTANT_MethodHandle:
+                bp = bp + 3;
+                break;
+            case CONSTANT_Fieldref:
+            case CONSTANT_Methodref:
+            case CONSTANT_InterfaceMethodref:
+            case CONSTANT_NameandType:
+            case CONSTANT_Integer:
+            case CONSTANT_Float:
+            case CONSTANT_InvokeDynamic:
+                bp = bp + 4;
+                break;
+            case CONSTANT_Long:
+            case CONSTANT_Double:
+                bp = bp + 8;
+                i++;
+                break;
+            default:
+                throw new BadClassFile("malformed constant pool");
+            }
+        }
+    }
+
+    /** Read the class name of a module-info.class file.
+     * The name is stored in a CONSTANT_Class entry, where the
+     * class name is of the form module-name/module-info.
+     */
+    String readModuleInfoName(int i) throws BadClassFile {
+        int classIndex = poolIdx[i];
+        if (buf[classIndex] == CONSTANT_Class) {
+            int utf8Index = poolIdx[getChar(classIndex + 1)];
+            if (buf[utf8Index] == CONSTANT_Utf8) {
+                int len = getChar(utf8Index + 1);
+                int start = utf8Index + 3;
+                String suffix = "/module-info";
+                if (endsWith(buf, start, len, suffix))
+                    return new String(ClassFile.internalize(buf, start, len - suffix.length()));
+            }
+        }
+        throw new BadClassFile("bad module-info name");
+    }
+
+    private boolean endsWith(byte[] buf, int start, int len, String suffix) {
+        if (len <= suffix.length())
+            return false;
+        for (int i = 0; i < suffix.length(); i++) {
+            if (buf[start + len - suffix.length() + i] != suffix.charAt(i))
+                return false;
+        }
+        return true;
+    }
+
+    private static byte[] readInputStream(byte[] buf, InputStream s) throws IOException {
+        try {
+            buf = ensureCapacity(buf, s.available());
+            int r = s.read(buf);
+            int bp = 0;
+            while (r != -1) {
+                bp += r;
+                buf = ensureCapacity(buf, bp);
+                r = s.read(buf, bp, buf.length - bp);
+            }
+            return buf;
+        } finally {
+            try {
+                s.close();
+            } catch (IOException e) {
+                /* Ignore any errors, as this stream may have already
+                 * thrown a related exception which is the one that
+                 * should be reported.
+                 */
+            }
+        }
+    }
+
+    /*
+     * ensureCapacity will increase the buffer as needed, taking note that
+     * the new buffer will always be greater than the needed and never
+     * exactly equal to the needed size or bp. If equal then the read (above)
+     * will infinitely loop as buf.length - bp == 0.
+     */
+    private static byte[] ensureCapacity(byte[] buf, int needed) {
+        if (buf.length <= needed) {
+            byte[] old = buf;
+            buf = new byte[Integer.highestOneBit(needed) << 1];
+            System.arraycopy(old, 0, buf, 0, old.length);
+        }
+        return buf;
+    }
+}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Tue Oct 04 13:41:52 2016 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Tue Oct 04 16:25:19 2016 +0200
@@ -37,6 +37,7 @@
 import java.util.Queue;
 import java.util.ResourceBundle;
 import java.util.Set;
+import java.util.function.Function;
 
 import javax.annotation.processing.Processor;
 import javax.lang.model.SourceVersion;
@@ -67,7 +68,9 @@
 import com.sun.tools.javac.tree.JCTree.JCLambda;
 import com.sun.tools.javac.tree.JCTree.JCMemberReference;
 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModuleDecl;
 import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.Tag;
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.util.DefinedBy.Api;
 import com.sun.tools.javac.util.JCDiagnostic.Factory;
@@ -341,6 +344,13 @@
                 }
             };
 
+    protected final ModuleFinder.ModuleInfoSourceFileCompleter moduleInfoSourceFileCompleter =
+            fo -> (ModuleSymbol) readSourceFile(parseImplicitFile(fo), null, tl -> {
+                return tl.defs.nonEmpty() && tl.defs.head.hasTag(Tag.MODULEDEF) ?
+                        ((JCModuleDecl) tl.defs.head).sym.module_info :
+                        syms.defineClass(names.module_info, syms.errModule);
+            }).owner;
+
     /**
      * Command line options.
      */
@@ -411,6 +421,7 @@
         diags = Factory.instance(context);
 
         finder.sourceCompleter = sourceCompleter;
+        moduleFinder.sourceFileCompleter = moduleInfoSourceFileCompleter;
 
         options = Options.instance(context);
 
@@ -779,6 +790,19 @@
         readSourceFile(null, c);
     }
 
+    private JCTree.JCCompilationUnit parseImplicitFile(JavaFileObject filename) {
+        JavaFileObject prev = log.useSource(filename);
+        try {
+            JCTree.JCCompilationUnit t = parse(filename, filename.getCharContent(false));
+            return t;
+        } catch (IOException e) {
+            log.error("error.reading.file", filename, JavacFileManager.getMessage(e));
+            return make.TopLevel(List.<JCTree>nil());
+        } finally {
+            log.useSource(prev);
+        }
+    }
+
     /** Compile a ClassSymbol from source, optionally using the given compilation unit as
      *  the source tree.
      *  @param tree the compilation unit in which the given ClassSymbol resides,
@@ -789,20 +813,20 @@
         if (completionFailureName == c.fullname) {
             throw new CompletionFailure(c, "user-selected completion failure by class name");
         }
-        JavaFileObject filename = c.classfile;
-        JavaFileObject prev = log.useSource(filename);
 
         if (tree == null) {
-            try {
-                tree = parse(filename, filename.getCharContent(false));
-            } catch (IOException e) {
-                log.error("error.reading.file", filename, JavacFileManager.getMessage(e));
-                tree = make.TopLevel(List.<JCTree>nil());
-            } finally {
-                log.useSource(prev);
-            }
+            tree = parseImplicitFile(c.classfile);
         }
 
+        readSourceFile(tree, c, cut -> c);
+    }
+
+    private ClassSymbol readSourceFile(JCCompilationUnit tree,
+                                       ClassSymbol expectedSymbol,
+                                       Function<JCCompilationUnit, ClassSymbol> symbolGetter)
+                                           throws CompletionFailure {
+        Assert.checkNonNull(tree);
+
         if (!taskListener.isEmpty()) {
             TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, tree);
             taskListener.started(e);
@@ -814,18 +838,20 @@
         // Note that if module resolution failed, we may not even
         // have enough modules available to access java.lang, and
         // so risk getting FatalError("no.java.lang") from MemberEnter.
-        if (!modules.enter(List.of(tree), c)) {
-            throw new CompletionFailure(c, diags.fragment("cant.resolve.modules"));
+        if (!modules.enter(List.of(tree), expectedSymbol)) {
+            throw new CompletionFailure(symbolGetter.apply(tree),
+                                        diags.fragment("cant.resolve.modules"));
         }
 
-        enter.complete(List.of(tree), c);
+        enter.complete(List.of(tree), expectedSymbol);
 
         if (!taskListener.isEmpty()) {
             TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, tree);
             taskListener.finished(e);
         }
 
-        if (enter.getEnv(c) == null) {
+        ClassSymbol sym = symbolGetter.apply(tree);
+        if (sym == null || enter.getEnv(sym) == null) {
             boolean isPkgInfo =
                 tree.sourcefile.isNameCompatible("package-info",
                                                  JavaFileObject.Kind.SOURCE);
@@ -836,24 +862,26 @@
                 if (enter.getEnv(tree.modle) == null) {
                     JCDiagnostic diag =
                         diagFactory.fragment("file.does.not.contain.module");
-                    throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory);
+                    throw new ClassFinder.BadClassFile(sym, tree.sourcefile, diag, diagFactory);
                 }
             } else if (isPkgInfo) {
                 if (enter.getEnv(tree.packge) == null) {
                     JCDiagnostic diag =
                         diagFactory.fragment("file.does.not.contain.package",
-                                                 c.location());
-                    throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory);
+                                                 sym.location());
+                    throw new ClassFinder.BadClassFile(sym, tree.sourcefile, diag, diagFactory);
                 }
             } else {
                 JCDiagnostic diag =
                         diagFactory.fragment("file.doesnt.contain.class",
-                                            c.getQualifiedName());
-                throw new ClassFinder.BadClassFile(c, filename, diag, diagFactory);
+                                            sym.getQualifiedName());
+                throw new ClassFinder.BadClassFile(sym, tree.sourcefile, diag, diagFactory);
             }
         }
 
         implicitSourceFilesRead = true;
+
+        return sym;
     }
 
     /** Track when the JavaCompiler has been used to compile something. */
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Tue Oct 04 13:41:52 2016 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Tue Oct 04 16:25:19 2016 +0200
@@ -2770,6 +2770,10 @@
 compiler.err.module.name.mismatch=\
     module name {0} does not match expected name {1}
 
+# 0: name, 1: name
+compiler.misc.module.name.mismatch=\
+    module name {0} does not match expected name {1}
+
 compiler.err.module.decl.sb.in.module-info.java=\
     module declarations should be in a file named module-info.java
 
--- a/langtools/test/tools/javac/diags/examples.not-yet.txt	Tue Oct 04 13:41:52 2016 +0200
+++ b/langtools/test/tools/javac/diags/examples.not-yet.txt	Tue Oct 04 16:25:19 2016 +0200
@@ -66,6 +66,7 @@
 compiler.misc.kindname.type.variable.bound
 compiler.misc.kindname.value
 compiler.misc.incompatible.eq.lower.bounds              # cannot happen?
+compiler.misc.module.name.mismatch
 compiler.misc.no.unique.minimal.instance.exists
 compiler.misc.no.unique.maximal.instance.exists         # cannot happen?
 compiler.misc.resume.abort                              # prompt for a response
--- a/langtools/test/tools/javac/modules/EdgeCases.java	Tue Oct 04 13:41:52 2016 +0200
+++ b/langtools/test/tools/javac/modules/EdgeCases.java	Tue Oct 04 16:25:19 2016 +0200
@@ -58,6 +58,8 @@
 import toolbox.JarTask;
 import toolbox.JavacTask;
 import toolbox.Task;
+import toolbox.Task.Expect;
+import toolbox.Task.OutputKind;
 
 public class EdgeCases extends ModuleTestBase {
 
@@ -304,4 +306,147 @@
         }
     }
 
+    @Test
+    public void testImplicitJavaBase(Path base) throws Exception {
+        Path src = base.resolve("src");
+        Path src_java_base = src.resolve("java.base");
+        Files.createDirectories(src_java_base);
+        tb.writeJavaFiles(src_java_base, "module java.base { exports java.lang; }");
+        tb.writeJavaFiles(src_java_base,
+                          "package java.lang; public class Object {}");
+        Path classes = base.resolve("classes");
+        tb.createDirectories(classes);
+
+        //module-info from source:
+        new JavacTask(tb)
+            .options("-sourcepath", src_java_base.toString())
+            .outdir(classes)
+            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
+            .run()
+            .writeAll();
+
+        //module-info from class:
+        if (!Files.exists(classes.resolve("module-info.class"))) {
+            throw new AssertionError("module-info.class not created!");
+        }
+
+        new JavacTask(tb)
+            .outdir(classes)
+            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
+            .run()
+            .writeAll();
+
+        //broken module-info.class:
+        Files.newOutputStream(classes.resolve("module-info.class")).close();
+
+        List<String> log = new JavacTask(tb)
+            .options("-XDrawDiagnostics")
+            .outdir(classes)
+            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
+            .run(Expect.FAIL)
+            .writeAll()
+            .getOutputLines(OutputKind.DIRECT);
+
+        List<String> expected = Arrays.asList(
+                "- compiler.err.cant.access: <error>.module-info, (compiler.misc.bad.class.file.header: module-info.class, (compiler.misc.illegal.start.of.class.file))",
+                "1 error");
+
+        if (!expected.equals(log)) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+
+        //broken module-info.java:
+        Files.delete(classes.resolve("module-info.class"));
+
+        try (Writer out = Files.newBufferedWriter(src_java_base.resolve("module-info.java"))) {
+            out.write("class Broken {}");
+        }
+
+        log = new JavacTask(tb)
+            .options("-sourcepath", src_java_base.toString(),
+                                "-XDrawDiagnostics")
+            .outdir(classes)
+            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
+            .run(Expect.FAIL)
+            .writeAll()
+            .getOutputLines(OutputKind.DIRECT);
+
+        expected = Arrays.asList("X");
+
+        if (expected.equals(log)) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+    }
+
+    @Test
+    public void testModuleInfoNameMismatchSource(Path base) throws Exception {
+        Path src = base.resolve("src");
+        Path m1 = src.resolve("m1");
+        Files.createDirectories(m1);
+        tb.writeJavaFiles(m1, "module other { }",
+                              "package test; public class Test {}");
+        Path classes = base.resolve("classes");
+        tb.createDirectories(classes);
+
+        List<String> log = new JavacTask(tb)
+            .options("--module-source-path", src.toString(),
+                     "-XDrawDiagnostics")
+            .outdir(classes)
+            .files(findJavaFiles(m1.resolve("test").resolve("Test.java")))
+            .run(Expect.FAIL)
+            .writeAll()
+            .getOutputLines(OutputKind.DIRECT);
+
+        List<String> expected = Arrays.asList(
+                "module-info.java:1:1: compiler.err.module.name.mismatch: other, m1",
+                "- compiler.err.cant.access: m1.module-info, (compiler.misc.cant.resolve.modules)",
+                "2 errors");
+
+        if (!expected.equals(log)) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+    }
+
+    @Test
+    public void testModuleInfoNameMismatchClass(Path base) throws Exception {
+        Path src = base.resolve("src");
+        Files.createDirectories(src);
+        tb.writeJavaFiles(src, "module other { }",
+                               "package test; public class Test {}");
+        Path classes = base.resolve("classes");
+        Path m1Classes = classes.resolve("m1");
+        tb.createDirectories(m1Classes);
+
+        new JavacTask(tb)
+            .outdir(m1Classes)
+            .files(findJavaFiles(src))
+            .run()
+            .writeAll()
+            .getOutputLines(OutputKind.DIRECT);
+
+        Path src2 = base.resolve("src2");
+        Files.createDirectories(src2);
+        tb.writeJavaFiles(src2, "module use { requires m1; }");
+
+        Path classes2 = base.resolve("classes2");
+        tb.createDirectories(classes2);
+
+        List<String> log = new JavacTask(tb)
+            .options("--module-path", classes.toString(),
+                     "-XDrawDiagnostics")
+            .outdir(classes2)
+            .files(findJavaFiles(src2))
+            .run(Expect.FAIL)
+            .writeAll()
+            .getOutputLines(OutputKind.DIRECT);
+
+        List<String> expected = Arrays.asList(
+                "- compiler.err.cant.access: m1.module-info, (compiler.misc.bad.class.file.header: module-info.class, (compiler.misc.module.name.mismatch: other, m1))",
+                "1 error");
+
+        if (!expected.equals(log)) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+    }
+
 }