1/*
2 * Copyright (c) 2016, 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 */
23package org.graalvm.compiler.core.common.util;
24
25import static org.graalvm.compiler.core.common.util.Util.JAVA_SPECIFICATION_VERSION;
26
27import java.lang.reflect.InvocationTargetException;
28import java.lang.reflect.Method;
29import java.lang.reflect.Modifier;
30
31/**
32 * Reflection based access to the Module API introduced by JDK 9. This allows the API to be used in
33 * code that must be compiled on a JDK prior to 9. Use of this class must be guarded by a test for
34 * JDK 9 or later. For example:
35 *
36 * <pre>
37 * if (Util.JAVA_SPECIFICATION_VERSION >= 9) {
38 *     // Use of ModuleAPI
39 * }
40 * </pre>
41 */
42public final class ModuleAPI {
43
44    private ModuleAPI(Method method) {
45        this.method = method;
46    }
47
48    private final Method method;
49
50    /**
51     * {@code Class.getModule()}.
52     */
53    public static final ModuleAPI getModule;
54
55    /**
56     * {@code java.lang.Module.getResourceAsStream(String)}.
57     */
58    public static final ModuleAPI getResourceAsStream;
59
60    /**
61     * {@code java.lang.Module.canRead(Module)}.
62     */
63    public static final ModuleAPI canRead;
64
65    /**
66     * {@code java.lang.Module.isExported(String)}.
67     */
68    public static final ModuleAPI isExported;
69
70    /**
71     * {@code java.lang.Module.isExported(String, Module)}.
72     */
73    public static final ModuleAPI isExportedTo;
74
75    /**
76     * Invokes the static Module API method represented by this object.
77     */
78    @SuppressWarnings("unchecked")
79    public <T> T invokeStatic(Object... args) {
80        checkAvailability();
81        assert Modifier.isStatic(method.getModifiers());
82        try {
83            return (T) method.invoke(null, args);
84        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
85            throw new InternalError(e);
86        }
87    }
88
89    /**
90     * Invokes the non-static Module API method represented by this object.
91     */
92    @SuppressWarnings("unchecked")
93    public <T> T invoke(Object receiver, Object... args) {
94        checkAvailability();
95        assert !Modifier.isStatic(method.getModifiers());
96        try {
97            return (T) method.invoke(receiver, args);
98        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
99            throw new InternalError(e);
100        }
101    }
102
103    private void checkAvailability() throws InternalError {
104        if (method == null) {
105            throw new InternalError("Cannot use Module API on JDK " + JAVA_SPECIFICATION_VERSION);
106        }
107    }
108
109    static {
110        if (JAVA_SPECIFICATION_VERSION >= 9) {
111            try {
112                getModule = new ModuleAPI(Class.class.getMethod("getModule"));
113                Class<?> moduleClass = getModule.method.getReturnType();
114                getResourceAsStream = new ModuleAPI(moduleClass.getMethod("getResourceAsStream", String.class));
115                canRead = new ModuleAPI(moduleClass.getMethod("canRead", moduleClass));
116                isExported = new ModuleAPI(moduleClass.getMethod("isExported", String.class));
117                isExportedTo = new ModuleAPI(moduleClass.getMethod("isExported", String.class, moduleClass));
118            } catch (NoSuchMethodException | SecurityException e) {
119                throw new InternalError(e);
120            }
121        } else {
122            ModuleAPI unavailable = new ModuleAPI(null);
123            getModule = unavailable;
124            getResourceAsStream = unavailable;
125            canRead = unavailable;
126            isExported = unavailable;
127            isExportedTo = unavailable;
128        }
129    }
130}
131