8193125: javac should not compile a module if it requires java.base with modifiers
authorjjg
Wed, 03 Jan 2018 11:10:56 -0800
changeset 48427 b08405cc467a
parent 48426 c08f1067ef57
child 48428 e569e83139fd
8193125: javac should not compile a module if it requires java.base with modifiers Reviewed-by: vromero
src/jdk.compiler/share/classes/com/sun/tools/javac/code/Directive.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java
src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
test/langtools/tools/javac/diags/examples.not-yet.txt
test/langtools/tools/javac/diags/examples/ModifierNotAllowed/module-info.java
test/langtools/tools/javac/modules/JavaBaseTest.java
test/langtools/tools/javac/processing/model/util/printing/module-info.java
test/langtools/tools/javac/processing/model/util/printing/module-info.out
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Directive.java	Wed Jan 03 15:37:35 2018 +0530
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Directive.java	Wed Jan 03 11:10:56 2018 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2018, 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
@@ -27,11 +27,13 @@
 
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.Locale;
 import java.util.Set;
 
 import javax.lang.model.element.ModuleElement;
 import javax.lang.model.element.ModuleElement.DirectiveVisitor;
 
+import com.sun.tools.javac.api.Messages;
 import com.sun.tools.javac.code.Symbol.ClassSymbol;
 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
 import com.sun.tools.javac.code.Symbol.PackageSymbol;
@@ -71,6 +73,11 @@
         }
 
         public final int value;
+
+        @Override
+        public String toString() {
+            return String.format("ACC_%s (0x%04x", name(), value);
+        }
     }
 
     /** Flags for ExportsDirective. */
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java	Wed Jan 03 15:37:35 2018 +0530
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java	Wed Jan 03 11:10:56 2018 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2018, 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
@@ -63,6 +63,7 @@
 import com.sun.tools.javac.code.Directive.RequiresFlag;
 import com.sun.tools.javac.code.Directive.UsesDirective;
 import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Flags.Flag;
 import com.sun.tools.javac.code.Lint.LintCategory;
 import com.sun.tools.javac.code.ModuleFinder;
 import com.sun.tools.javac.code.Source;
@@ -109,11 +110,15 @@
 import static com.sun.tools.javac.code.Flags.ENUM;
 import static com.sun.tools.javac.code.Flags.PUBLIC;
 import static com.sun.tools.javac.code.Flags.UNATTRIBUTED;
+
 import com.sun.tools.javac.code.Kinds;
+
 import static com.sun.tools.javac.code.Kinds.Kind.ERR;
 import static com.sun.tools.javac.code.Kinds.Kind.MDL;
 import static com.sun.tools.javac.code.Kinds.Kind.MTH;
+
 import com.sun.tools.javac.code.Symbol.ModuleResolutionFlags;
+
 import static com.sun.tools.javac.code.TypeTag.CLASS;
 
 /**
@@ -775,10 +780,20 @@
             } else {
                 allRequires.add(msym);
                 Set<RequiresFlag> flags = EnumSet.noneOf(RequiresFlag.class);
-                if (tree.isTransitive)
-                    flags.add(RequiresFlag.TRANSITIVE);
-                if (tree.isStaticPhase)
-                    flags.add(RequiresFlag.STATIC_PHASE);
+                if (tree.isTransitive) {
+                    if (msym == syms.java_base && source.compareTo(Source.JDK10) >= 0) {
+                        log.error(tree.pos(), Errors.ModifierNotAllowedHere(names.transitive));
+                    } else {
+                        flags.add(RequiresFlag.TRANSITIVE);
+                    }
+                }
+                if (tree.isStaticPhase) {
+                    if (msym == syms.java_base && source.compareTo(Source.JDK10) >= 0) {
+                        log.error(tree.pos(), Errors.ModNotAllowedHere(EnumSet.of(Flag.STATIC)));
+                    } else {
+                        flags.add(RequiresFlag.STATIC_PHASE);
+                    }
+                }
                 RequiresDirective d = new RequiresDirective(msym, flags);
                 tree.directive = d;
                 sym.requires = sym.requires.prepend(d);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Wed Jan 03 15:37:35 2018 +0530
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Wed Jan 03 11:10:56 2018 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, 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
@@ -1315,7 +1315,8 @@
                             throw badClassFile("module.name.mismatch", moduleName, currentModule.name);
                         }
 
-                        msym.flags.addAll(readModuleFlags(nextChar()));
+                        Set<ModuleFlags> moduleFlags = readModuleFlags(nextChar());
+                        msym.flags.addAll(moduleFlags);
                         msym.version = readName(nextChar());
 
                         ListBuffer<RequiresDirective> requires = new ListBuffer<>();
@@ -1323,6 +1324,14 @@
                         for (int i = 0; i < nrequires; i++) {
                             ModuleSymbol rsym = syms.enterModule(readModuleName(nextChar()));
                             Set<RequiresFlag> flags = readRequiresFlags(nextChar());
+                            if (rsym == syms.java_base && majorVersion >= V54.major) {
+                                if (flags.contains(RequiresFlag.TRANSITIVE)) {
+                                    throw badClassFile("bad.requires.flag", RequiresFlag.TRANSITIVE);
+                                }
+                                if (flags.contains(RequiresFlag.STATIC_PHASE)) {
+                                    throw badClassFile("bad.requires.flag", RequiresFlag.STATIC_PHASE);
+                                }
+                            }
                             nextChar(); // skip compiled version
                             requires.add(new RequiresDirective(rsym, flags));
                         }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Wed Jan 03 15:37:35 2018 +0530
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Wed Jan 03 11:10:56 2018 -0800
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 1999, 2018, 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
@@ -753,6 +753,10 @@
 compiler.err.mod.not.allowed.here=\
     modifier {0} not allowed here
 
+# 0: name
+compiler.err.modifier.not.allowed.here=\
+    modifier {0} not allowed here
+
 compiler.err.intf.not.allowed.here=\
     interface not allowed here
 
@@ -3131,6 +3135,9 @@
 compiler.misc.cant.resolve.modules=\
     cannot resolve modules
 
+compiler.misc.bad.requires.flag=\
+    bad requires flag: {0}
+
 # 0: string
 compiler.err.invalid.module.specifier=\
     module specifier not allowed: {0}
--- a/test/langtools/tools/javac/diags/examples.not-yet.txt	Wed Jan 03 15:37:35 2018 +0530
+++ b/test/langtools/tools/javac/diags/examples.not-yet.txt	Wed Jan 03 11:10:56 2018 -0800
@@ -52,6 +52,7 @@
 compiler.misc.bad.enclosing.method                      # bad class file
 compiler.misc.bad.runtime.invisible.param.annotations   # bad class file
 compiler.misc.bad.signature                             # bad class file
+compiler.misc.bad.requires.flag				# bad class file
 compiler.misc.bad.type.annotation.value
 compiler.misc.base.membership                           # UNUSED
 compiler.misc.class.file.not.found                      # ClassReader
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/diags/examples/ModifierNotAllowed/module-info.java	Wed Jan 03 11:10:56 2018 -0800
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ * 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.
+ */
+
+// key: compiler.err.modifier.not.allowed.here
+
+module m {
+     requires transitive java.base;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/modules/JavaBaseTest.java	Wed Jan 03 11:10:56 2018 -0800
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2017, 2018, 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.
+ *
+ * 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.
+ */
+
+/**
+ * @test
+ * @bug 8193125
+ * @summary test modifiers with java.base
+ * @library /tools/lib
+ * @modules
+ *      jdk.compiler/com.sun.tools.javac.api
+ *      jdk.compiler/com.sun.tools.javac.main
+ *      jdk.jdeps/com.sun.tools.classfile
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @run main JavaBaseTest
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.tools.classfile.Attribute;
+import com.sun.tools.classfile.Attributes;
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.ClassWriter;
+import com.sun.tools.classfile.Module_attribute;
+
+import toolbox.JavacTask;
+import toolbox.Task;
+import toolbox.Task.Expect;
+import toolbox.ToolBox;
+
+public class JavaBaseTest {
+
+    public static void main(String... args) throws Exception {
+        JavaBaseTest t = new JavaBaseTest();
+        t.run();
+    }
+
+    final List<List<String>> modifiers = List.of(
+        List.of("static"),
+        List.of("transitive"),
+        List.of("static", "transitive")
+    );
+
+    final String specVersion = System.getProperty("java.specification.version");
+    final List<String> targets = specVersion.equals("10")
+            ? List.of("9", "10")
+            : List.of("9", "10", specVersion);
+
+    enum Mode { SOURCE, CLASS };
+
+    ToolBox tb = new ToolBox();
+    int testCount = 0;
+    int errorCount = 0;
+
+    void run() throws Exception {
+        for (List<String> mods : modifiers) {
+            for (String target : targets) {
+                for (Mode mode : Mode.values()) {
+                    test(mods, target, mode);
+                }
+            }
+        }
+
+        System.err.println(testCount + " tests");
+        if (errorCount > 0) {
+            throw new Exception(errorCount + " errors found");
+        }
+    }
+
+    void test(List<String> mods, String target, Mode mode) {
+        System.err.println("Test " + mods + " " + target + " " + mode);
+        testCount++;
+        try {
+            Path base = Paths.get(String.join("-", mods))
+                    .resolve(target).resolve(mode.name().toLowerCase());
+            Files.createDirectories(base);
+            switch (mode) {
+                case SOURCE:
+                    testSource(base, mods, target);
+                    break;
+                case CLASS:
+                    testClass(base, mods, target);
+                    break;
+            }
+        } catch (Exception e) {
+            error("Exception: " + e);
+            e.printStackTrace();
+        }
+        System.err.println();
+    }
+
+    void testSource(Path base, List<String> mods, String target) throws Exception {
+        Path src = base.resolve("src");
+        tb.writeJavaFiles(src,
+                "module m { requires " + String.join(" ", mods) + " java.base; }");
+        Path modules = Files.createDirectories(base.resolve("modules"));
+        boolean expectOK = target.equals("9");
+
+        String log = new JavacTask(tb)
+                .outdir(modules)
+                .options("-XDrawDiagnostics", "--release", target)
+                .files(tb.findJavaFiles(src))
+                .run(expectOK ? Task.Expect.SUCCESS : Task.Expect.FAIL)
+                .writeAll()
+                .getOutput(Task.OutputKind.DIRECT);
+
+        if (!expectOK) {
+            boolean foundErrorMessage = false;
+            for (String mod : mods) {
+                String key = mod.equals("static")
+                    ? "compiler.err.mod.not.allowed.here"
+                    : "compiler.err.modifier.not.allowed.here";
+                String message = "module-info.java:1:12: " + key + ": " + mod;
+                if (log.contains(message)) {
+                    foundErrorMessage = true;
+                }
+            }
+            if (!foundErrorMessage) {
+                throw new Exception("expected error message not found");
+            }
+        }
+    }
+
+    void testClass(Path base, List<String> mods, String target) throws Exception {
+        createClass(base, mods, target);
+
+        Path src = base.resolve("src");
+        tb.writeJavaFiles(src,
+                "module mx { requires m; }");
+        Path modules = Files.createDirectories(base.resolve("modules"));
+
+        boolean expectOK = target.equals("9");
+        String log = new JavacTask(tb)
+                .outdir(modules)
+                .options("-XDrawDiagnostics",
+                        "--module-path", base.resolve("test-modules").toString())
+                .files(tb.findJavaFiles(src))
+                .run(expectOK ? Task.Expect.SUCCESS : Task.Expect.FAIL)
+                .writeAll()
+                .getOutput(Task.OutputKind.DIRECT);
+
+        if (!expectOK) {
+            boolean foundErrorMessage = false;
+            for (String mod : mods) {
+                String flag = mod.equals("static")
+                    ? "ACC_STATIC_PHASE (0x0040)"
+                    : "ACC_TRANSITIVE (0x0020)";
+                String message = "- compiler.err.cant.access: m.module-info,"
+                        + " (compiler.misc.bad.class.file.header: module-info.class,"
+                        + " (compiler.misc.bad.requires.flag: " + flag + ")";
+                if (log.contains(message)) {
+                    foundErrorMessage = true;
+                }
+            }
+            if (!foundErrorMessage) {
+                throw new Exception("expected error message not found");
+            }
+        }
+    }
+
+    void createClass(Path base, List<String> mods, String target) throws Exception {
+        Path src1 = base.resolve("interim-src");
+        tb.writeJavaFiles(src1,
+                "module m { requires java.base; }");
+        Path modules1 = Files.createDirectories(base.resolve("interim-modules"));
+
+        new JavacTask(tb)
+                .outdir(modules1)
+                .options("--release", target)
+                .files(tb.findJavaFiles(src1))
+                .run(Task.Expect.SUCCESS)
+                .writeAll();
+
+        ClassFile cf1 = ClassFile.read(modules1.resolve("module-info.class"));
+
+        Map<String,Attribute> attrMap = new LinkedHashMap<>(cf1.attributes.map);
+        Module_attribute modAttr1 = (Module_attribute) attrMap.get("Module");
+        Module_attribute.RequiresEntry[] requires =
+                new Module_attribute.RequiresEntry[modAttr1.requires_count];
+        for (int i = 0; i < modAttr1.requires_count; i++) {
+            Module_attribute.RequiresEntry e1 = modAttr1.requires[i];
+            int flags = e1.requires_flags;
+            Module_attribute.RequiresEntry e2;
+            if (e1.getRequires(cf1.constant_pool).equals("java.base")) {
+                for (String mod : mods) {
+                    switch (mod) {
+                        case "static":
+                            flags |= Module_attribute.ACC_STATIC_PHASE;
+                            break;
+                        case "transitive":
+                            flags |= Module_attribute.ACC_TRANSITIVE;
+                            break;
+                    }
+                }
+                e2 = new Module_attribute.RequiresEntry(
+                        e1.requires_index,
+                        flags,
+                        e1.requires_version_index);
+            } else {
+                e2 = e1;
+            }
+            requires[i] = e2;
+        }
+        Module_attribute modAttr2 = new Module_attribute(
+                modAttr1.attribute_name_index,
+                modAttr1.module_name,
+                modAttr1.module_flags,
+                modAttr1.module_version_index,
+                requires,
+                modAttr1.exports,
+                modAttr1.opens,
+                modAttr1.uses_index,
+                modAttr1.provides);
+        attrMap.put("Module", modAttr2);
+        Attributes attributes = new Attributes(attrMap);
+
+        ClassFile cf2 = new ClassFile(
+                cf1.magic, cf1.minor_version, cf1.major_version,
+                cf1.constant_pool, cf1.access_flags,
+                cf1.this_class, cf1.super_class, cf1.interfaces,
+                cf1.fields, cf1.methods, attributes);
+        Path modInfo = base.resolve("test-modules").resolve("module-info.class");
+        Files.createDirectories(modInfo.getParent());
+        new ClassWriter().write(cf2, modInfo.toFile());
+    }
+
+    private void error(String message) {
+        System.err.println("Error: " + message);
+        errorCount++;
+    }
+}
+
--- a/test/langtools/tools/javac/processing/model/util/printing/module-info.java	Wed Jan 03 15:37:35 2018 +0530
+++ b/test/langtools/tools/javac/processing/model/util/printing/module-info.java	Wed Jan 03 11:10:56 2018 -0800
@@ -1,3 +1,26 @@
+/*
+ * Copyright (c) 2017, 2018, 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.
+ *
+ * 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.
+ */
+
 /*
  * @test
  * @bug 8173609
@@ -10,7 +33,7 @@
  */
 @Deprecated
 module printing {
-    requires static transitive java.base;
+    requires static transitive java.compiler;
     exports p to m.m1, m.m2;
     opens p to m.m1, m.m2;
     uses p.P;
--- a/test/langtools/tools/javac/processing/model/util/printing/module-info.out	Wed Jan 03 15:37:35 2018 +0530
+++ b/test/langtools/tools/javac/processing/model/util/printing/module-info.out	Wed Jan 03 11:10:56 2018 -0800
@@ -19,7 +19,8 @@
  */
 @java.lang.Deprecated
 module printing {
-  requires static transitive java.base;
+  requires java.base;
+  requires static transitive java.compiler;
   exports p to m.m1, m.m2;
   opens p to m.m1, m.m2;
   uses p.P;