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.Collections;
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:/"),
71                                                  Collections.emptyMap());
72        Path path = fs.getPath("/", "modules", modName, "module-info.class");
73        try (InputStream in = Files.newInputStream(path)) {
74            return hasModuleTarget(in);
75        }
76    }
77
78    // the system module plugin by default drops ModuleTarget attribute
79    private static boolean expectModuleTarget = false;
80    public static void main(String... args) throws IOException {
81        if (args.length > 0) {
82            if (!args[0].equals("retainModuleTarget")) {
83                throw new IllegalArgumentException(args[0]);
84            }
85
86            expectModuleTarget = true;
87        }
88
89        // java.base is packaged with osName/osArch/osVersion
90        if (! hasModuleTarget("java.base")) {
91            throw new RuntimeException("ModuleTarget absent for java.base");
92        }
93
94        // verify module-info.class for m1 and m4
95        checkModule("m1", "p1", "p2");
96        checkModule("m4", "p4");
97    }
98
99    private static void checkModule(String mn, String... packages) throws IOException {
100        // verify ModuleDescriptor from the runtime module
101        ModuleDescriptor md = ModuleLayer.boot().findModule(mn).get()
102                                   .getDescriptor();
103        checkModuleDescriptor(md, packages);
104
105        // verify ModuleDescriptor from module-info.class read from ModuleReader
106        try (InputStream in = ModuleFinder.ofSystem().find(mn).get()
107            .open().open("module-info.class").get()) {
108            checkModuleDescriptor(ModuleDescriptor.read(in), packages);
109        }
110
111        // verify ModuleDescriptor from module-info.class read from jimage
112        FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"),
113            Collections.emptyMap());
114        Path path = fs.getPath("/", "modules", mn, "module-info.class");
115        checkModuleDescriptor(ModuleDescriptor.read(Files.newInputStream(path)), packages);
116    }
117
118    static void checkModuleDescriptor(ModuleDescriptor md, String... packages) throws IOException {
119        String mainClass = md.name().replace('m', 'p') + ".Main";
120        if (!md.mainClass().get().equals(mainClass)) {
121            throw new RuntimeException(md.mainClass().toString());
122        }
123
124        if (expectModuleTarget) {
125            // ModuleTarget attribute is retained
126            if (! hasModuleTarget(md.name())) {
127                throw new RuntimeException("ModuleTarget missing for " + md.name());
128            }
129        } else {
130            // by default ModuleTarget attribute is dropped
131            if (hasModuleTarget(md.name())) {
132                throw new RuntimeException("ModuleTarget present for " + md.name());
133            }
134        }
135
136        Set<String> pkgs = md.packages();
137        if (!pkgs.equals(Set.of(packages))) {
138            throw new RuntimeException(pkgs + " expected: " + Set.of(packages));
139        }
140    }
141}
142