ModuleAPI.java revision 13012:bb6c49bb6d6d
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.AccessibleObject;
28import java.lang.reflect.InvocationTargetException;
29import java.lang.reflect.Method;
30import java.lang.reflect.Modifier;
31
32/**
33 * Reflection based access to the Module API introduced by JDK 9. This allows the API to be used in
34 * code that must be compiled on a JDK prior to 9. Use of this class must be guarded by a test for
35 * JDK 9 or later. For example:
36 *
37 * <pre>
38 * if (Util.JAVA_SPECIFICATION_VERSION >= 9) {
39 *     // Use of ModuleAPI
40 * }
41 * </pre>
42 */
43public final class ModuleAPI {
44
45    private ModuleAPI(Method method) {
46        this.method = method;
47    }
48
49    private final Method method;
50
51    /**
52     * {@code Class.getModule()}.
53     */
54    public static final ModuleAPI getModule;
55
56    /**
57     * {@code jdk.internal.module.Modules.addExports(Module, String, Module)}.
58     */
59    public static final ModuleAPI addExports;
60
61    /**
62     * {@code jdk.internal.module.Modules.addOpens(Module, String, Module)}.
63     */
64    public static final ModuleAPI addOpens;
65
66    /**
67     * {@code java.lang.Module.getResourceAsStream(String)}.
68     */
69    public static final ModuleAPI getResourceAsStream;
70
71    /**
72     * {@code java.lang.Module.getPackages()}.
73     */
74    public static final ModuleAPI getPackages;
75
76    /**
77     * {@code java.lang.Module.canRead(Module)}.
78     */
79    public static final ModuleAPI canRead;
80
81    /**
82     * {@code java.lang.Module.isExported(String)}.
83     */
84    public static final ModuleAPI isExported;
85
86    /**
87     * {@code java.lang.Module.isExported(String, Module)}.
88     */
89    public static final ModuleAPI isExportedTo;
90
91    /**
92     * Invokes the static Module API method represented by this object.
93     */
94    @SuppressWarnings("unchecked")
95    public <T> T invokeStatic(Object... args) {
96        checkAvailability();
97        assert Modifier.isStatic(method.getModifiers());
98        try {
99            return (T) method.invoke(null, args);
100        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
101            throw new InternalError(e);
102        }
103    }
104
105    /**
106     * Invokes the non-static Module API method represented by this object.
107     */
108    @SuppressWarnings("unchecked")
109    public <T> T invoke(Object receiver, Object... args) {
110        checkAvailability();
111        assert !Modifier.isStatic(method.getModifiers());
112        try {
113            return (T) method.invoke(receiver, args);
114        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
115            throw new InternalError(e);
116        }
117    }
118
119    /**
120     * Opens all packages in {@code moduleMember}'s module for deep reflection (i.e., allow
121     * {@link AccessibleObject#setAccessible(boolean)} to be called for any class/method/field) by
122     * {@code requestor}'s module.
123     */
124    public static void openAllPackagesForReflectionTo(Class<?> moduleMember, Class<?> requestor) {
125        Object moduleToOpen = getModule.invoke(moduleMember);
126        Object requestorModule = getModule.invoke(requestor);
127        if (moduleToOpen != requestorModule) {
128            String[] packages = getPackages.invoke(moduleToOpen);
129            for (String pkg : packages) {
130                addOpens.invokeStatic(moduleToOpen, pkg, requestorModule);
131            }
132        }
133    }
134
135    /**
136     * Opens {@code declaringClass}'s package to allow a method declared in {@code accessor} to call
137     * {@link AccessibleObject#setAccessible(boolean)} on an {@link AccessibleObject} representing a
138     * field or method declared by {@code declaringClass}.
139     */
140    public static void openForReflectionTo(Class<?> declaringClass, Class<?> accessor) {
141        Object moduleToOpen = getModule.invoke(declaringClass);
142        Object accessorModule = getModule.invoke(accessor);
143        if (moduleToOpen != accessorModule) {
144            addOpens.invokeStatic(moduleToOpen, declaringClass.getPackage().getName(), accessorModule);
145        }
146    }
147
148    /**
149     * Exports the package named {@code packageName} declared in {@code moduleMember}'s module to
150     * {@code requestor}'s module.
151     */
152    public static void exportPackageTo(Class<?> moduleMember, String packageName, Class<?> requestor) {
153        Object moduleToExport = getModule.invoke(moduleMember);
154        Object requestorModule = getModule.invoke(requestor);
155        if (moduleToExport != requestorModule) {
156            addExports.invokeStatic(moduleToExport, packageName, requestorModule);
157        }
158    }
159
160    private void checkAvailability() throws InternalError {
161        if (method == null) {
162            throw new InternalError("Cannot use Module API on JDK " + JAVA_SPECIFICATION_VERSION);
163        }
164    }
165
166    static {
167        if (JAVA_SPECIFICATION_VERSION >= 9) {
168            try {
169                getModule = new ModuleAPI(Class.class.getMethod("getModule"));
170                Class<?> moduleClass = getModule.method.getReturnType();
171                Class<?> modulesClass = Class.forName("jdk.internal.module.Modules");
172                getResourceAsStream = new ModuleAPI(moduleClass.getMethod("getResourceAsStream", String.class));
173                getPackages = new ModuleAPI(moduleClass.getMethod("getPackages"));
174                canRead = new ModuleAPI(moduleClass.getMethod("canRead", moduleClass));
175                isExported = new ModuleAPI(moduleClass.getMethod("isExported", String.class));
176                isExportedTo = new ModuleAPI(moduleClass.getMethod("isExported", String.class, moduleClass));
177                addExports = new ModuleAPI(modulesClass.getDeclaredMethod("addExports", moduleClass, String.class, moduleClass));
178                addOpens = new ModuleAPI(modulesClass.getDeclaredMethod("addOpens", moduleClass, String.class, moduleClass));
179            } catch (NoSuchMethodException | SecurityException | ClassNotFoundException e) {
180                throw new InternalError(e);
181            }
182        } else {
183            ModuleAPI unavailable = new ModuleAPI(null);
184            getModule = unavailable;
185            getResourceAsStream = unavailable;
186            getPackages = unavailable;
187            canRead = unavailable;
188            isExported = unavailable;
189            isExportedTo = unavailable;
190            addExports = unavailable;
191            addOpens = unavailable;
192        }
193
194    }
195}
196