1 /*
   2  * Copyright (c) 2016, 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 package org.graalvm.compiler.serviceprovider;
  24 
  25 import java.lang.reflect.InvocationTargetException;
  26 import java.lang.reflect.Method;
  27 import java.lang.reflect.Modifier;
  28 
  29 /**
  30  * Reflection based access to API introduced by JDK 9. This allows the API to be used in code that
  31  * must be compiled on a JDK prior to 9.
  32  */
  33 public final class JDK9Method {
  34 
  35     private static int getJavaSpecificationVersion() {
  36         String value = System.getProperty("java.specification.version");
  37         if (value.startsWith("1.")) {
  38             value = value.substring(2);
  39         }
  40         return Integer.parseInt(value);
  41     }
  42 
  43     /**
  44      * The integer value corresponding to the value of the {@code java.specification.version} system
  45      * property after any leading {@code "1."} has been stripped.
  46      */
  47     public static final int JAVA_SPECIFICATION_VERSION = getJavaSpecificationVersion();
  48 
  49     public JDK9Method(Class<?> declaringClass, String name, Class<?>... parameterTypes) {
  50         try {
  51             this.method = declaringClass.getMethod(name, parameterTypes);
  52         } catch (Exception e) {
  53             throw new InternalError(e);
  54         }
  55     }
  56 
  57     /**
  58      * Determines if the Java runtime is version 8 or earlier.
  59      */
  60     public static final boolean Java8OrEarlier = JAVA_SPECIFICATION_VERSION <= 8;
  61 
  62     public final Method method;
  63 
  64     public Class<?> getReturnType() {
  65         return method.getReturnType();
  66     }
  67 
  68     /**
  69      * {@code Class.getModule()}.
  70      */
  71     public static final JDK9Method getModule;
  72 
  73     /**
  74      * {@code java.lang.Module.getPackages()}.
  75      */
  76     public static final JDK9Method getPackages;
  77 
  78     /**
  79      * {@code java.lang.Module.getResourceAsStream(String)}.
  80      */
  81     public static final JDK9Method getResourceAsStream;
  82 
  83     /**
  84      * {@code java.lang.Module.addOpens(String, Module)}.
  85      */
  86     public static final JDK9Method addOpens;
  87 
  88     /**
  89      * {@code java.lang.Module.isOpen(String, Module)}.
  90      */
  91     public static final JDK9Method isOpenTo;
  92 
  93     /**
  94      * Invokes the static Module API method represented by this object.
  95      */
  96     @SuppressWarnings("unchecked")
  97     public <T> T invokeStatic(Object... args) {
  98         checkAvailability();
  99         assert Modifier.isStatic(method.getModifiers());
 100         try {
 101             return (T) method.invoke(null, args);
 102         } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
 103             throw new InternalError(e);
 104         }
 105     }
 106 
 107     /**
 108      * Invokes the non-static Module API method represented by this object.
 109      */
 110     @SuppressWarnings("unchecked")
 111     public <T> T invoke(Object receiver, Object... args) {
 112         checkAvailability();
 113         assert !Modifier.isStatic(method.getModifiers());
 114         try {
 115             return (T) method.invoke(receiver, args);
 116         } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
 117             throw new InternalError(e);
 118         }
 119     }
 120 
 121     private void checkAvailability() throws InternalError {
 122         if (method == null) {
 123             throw new InternalError("Cannot use Module API on JDK " + JAVA_SPECIFICATION_VERSION);
 124         }
 125     }
 126 
 127     static {
 128         if (JAVA_SPECIFICATION_VERSION >= 9) {
 129             getModule = new JDK9Method(Class.class, "getModule");
 130             Class<?> moduleClass = getModule.getReturnType();
 131             getPackages = new JDK9Method(moduleClass, "getPackages");
 132             addOpens = new JDK9Method(moduleClass, "addOpens", String.class, moduleClass);
 133             getResourceAsStream = new JDK9Method(moduleClass, "getResourceAsStream", String.class);
 134             isOpenTo = new JDK9Method(moduleClass, "isOpen", String.class, moduleClass);
 135         } else {
 136             JDK9Method unavailable = new JDK9Method();
 137             getModule = unavailable;
 138             getPackages = unavailable;
 139             addOpens = unavailable;
 140             getResourceAsStream = unavailable;
 141             isOpenTo = unavailable;
 142         }
 143     }
 144 
 145     private JDK9Method() {
 146         method = null;
 147     }
 148 }