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
--- 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);
+ }
+ }
+
}