1/*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24package p4;
25
26import java.io.IOException;
27import java.io.InputStream;
28import java.lang.module.ModuleDescriptor;
29import java.lang.module.ModuleFinder;
30import java.net.URI;
31import java.nio.file.FileSystem;
32import java.nio.file.FileSystems;
33import java.nio.file.Files;
34import java.nio.file.Path;
35import java.util.Map;
36import java.util.Set;
37
38import jdk.internal.module.ClassFileAttributes;
39import jdk.internal.module.ClassFileAttributes.ModuleTargetAttribute;
40import jdk.internal.module.ClassFileConstants;
41import jdk.internal.org.objectweb.asm.Attribute;
42import jdk.internal.org.objectweb.asm.ClassReader;
43import jdk.internal.org.objectweb.asm.ClassVisitor;
44import jdk.internal.org.objectweb.asm.Opcodes;
45
46public class Main {
47    private static boolean hasModuleTarget(InputStream in) throws IOException {
48        ModuleTargetAttribute[] modTargets = new ModuleTargetAttribute[1];
49        ClassVisitor cv = new ClassVisitor(Opcodes.ASM5) {
50            @Override
51            public void visitAttribute(Attribute attr) {
52                if (attr instanceof ModuleTargetAttribute) {
53                    modTargets[0] = (ModuleTargetAttribute)attr;
54                }
55            }
56        };
57
58        // prototype of attributes that should be parsed
59        Attribute[] attrs = new Attribute[] {
60            new ModuleTargetAttribute()
61        };
62
63        // parse module-info.class
64        ClassReader cr = new ClassReader(in);
65        cr.accept(cv, attrs, 0);
66        return modTargets[0] != null && modTargets[0].targetPlatform() != null;
67    }
68
69    private static boolean hasModuleTarget(String modName) throws IOException {
70        FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Map.of());
71        Path path = fs.getPath("/", "modules", modName, "module-info.class");
72        try (InputStream in = Files.newInputStream(path)) {
73            return hasModuleTarget(in);
74        }
75    }
76
77    // the system module plugin by default drops ModuleTarget attribute
78    private static boolean expectModuleTarget = false;
79    public static void main(String... args) throws IOException {
80        if (args.length > 0) {
81            if (!args[0].equals("retainModuleTarget")) {
82                throw new IllegalArgumentException(args[0]);
83            }
84
85            expectModuleTarget = true;
86        }
87
88        // java.base is packaged with ModuleTarget
89        if (!hasModuleTarget("java.base")) {
90            throw new RuntimeException("ModuleTarget absent for java.base");
91        }
92
93        // verify module-info.class for m1 and m4
94        checkModule("m1", "p1", "p2");
95        checkModule("m4", "p4");
96    }
97
98    private static void checkModule(String mn, String... packages) throws IOException {
99        // verify ModuleDescriptor from the runtime module
100        ModuleDescriptor md = ModuleLayer.boot().findModule(mn).get()
101                                   .getDescriptor();
102        checkModuleDescriptor(md, packages);
103
104        // verify ModuleDescriptor from module-info.class read from ModuleReader
105        try (InputStream in = ModuleFinder.ofSystem().find(mn).get()
106            .open().open("module-info.class").get()) {
107            checkModuleDescriptor(ModuleDescriptor.read(in), packages);
108        }
109
110        // verify ModuleDescriptor from module-info.class read from jimage
111        FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Map.of());
112        Path path = fs.getPath("/", "modules", mn, "module-info.class");
113        checkModuleDescriptor(ModuleDescriptor.read(Files.newInputStream(path)), packages);
114    }
115
116    static void checkModuleDescriptor(ModuleDescriptor md, String... packages) throws IOException {
117        String mainClass = md.name().replace('m', 'p') + ".Main";
118        if (!md.mainClass().get().equals(mainClass)) {
119            throw new RuntimeException(md.mainClass().toString());
120        }
121
122        // ModuleTarget attribute should be present
123        if (!hasModuleTarget(md.name())) {
124            throw new RuntimeException("ModuleTarget missing for " + md.name());
125        }
126
127        Set<String> pkgs = md.packages();
128        if (!pkgs.equals(Set.of(packages))) {
129            throw new RuntimeException(pkgs + " expected: " + Set.of(packages));
130        }
131    }
132}
133