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 */ 23package org.graalvm.compiler.serviceprovider; 24 25import static org.graalvm.compiler.serviceprovider.JDK9Method.Java8OrEarlier; 26import static org.graalvm.compiler.serviceprovider.JDK9Method.addOpens; 27import static org.graalvm.compiler.serviceprovider.JDK9Method.getModule; 28import static org.graalvm.compiler.serviceprovider.JDK9Method.getPackages; 29import static org.graalvm.compiler.serviceprovider.JDK9Method.isOpenTo; 30 31import java.lang.reflect.Method; 32import java.util.Iterator; 33import java.util.ServiceConfigurationError; 34import java.util.ServiceLoader; 35import java.util.Set; 36 37import jdk.vm.ci.services.JVMCIPermission; 38import jdk.vm.ci.services.Services; 39 40/** 41 * A mechanism for accessing service providers that abstracts over whether Graal is running on 42 * JVMCI-8 or JVMCI-9. In JVMCI-8, a JVMCI specific mechanism is used to lookup services via the 43 * hidden JVMCI class loader. In JVMCI-9, the standard {@link ServiceLoader} mechanism is used. 44 */ 45public final class GraalServices { 46 47 private GraalServices() { 48 } 49 50 /** 51 * Opens all JVMCI packages to the module of a given class. This relies on JVMCI already having 52 * opened all its packages to the module defining {@link GraalServices}. 53 * 54 * @param other all JVMCI packages will be opened to the module defining this class 55 */ 56 public static void openJVMCITo(Class<?> other) { 57 Object jvmci = getModule.invoke(Services.class); 58 Object otherModule = getModule.invoke(other); 59 if (jvmci != otherModule) { 60 Set<String> packages = getPackages.invoke(jvmci); 61 for (String pkg : packages) { 62 boolean opened = isOpenTo.invoke(jvmci, pkg, otherModule); 63 if (!opened) { 64 addOpens.invoke(jvmci, pkg, otherModule); 65 } 66 } 67 } 68 } 69 70 /** 71 * Gets an {@link Iterable} of the providers available for a given service. 72 * 73 * @throws SecurityException if on JDK8 and a security manager is present and it denies 74 * {@link JVMCIPermission} 75 */ 76 public static <S> Iterable<S> load(Class<S> service) { 77 assert !service.getName().startsWith("jdk.vm.ci") : "JVMCI services must be loaded via " + Services.class.getName(); 78 if (Java8OrEarlier) { 79 return load8(service); 80 } 81 Iterable<S> iterable = ServiceLoader.load(service); 82 return new Iterable<S>() { 83 @Override 84 public Iterator<S> iterator() { 85 Iterator<S> iterator = iterable.iterator(); 86 return new Iterator<S>() { 87 @Override 88 public boolean hasNext() { 89 return iterator.hasNext(); 90 } 91 92 @Override 93 public S next() { 94 S provider = iterator.next(); 95 // Allow Graal extensions to access JVMCI 96 openJVMCITo(provider.getClass()); 97 return provider; 98 } 99 100 @Override 101 public void remove() { 102 iterator.remove(); 103 } 104 }; 105 } 106 }; 107 } 108 109 /** 110 * {@code Services.load(Class)} is only defined in JVMCI-8. 111 */ 112 private static volatile Method loadMethod; 113 114 @SuppressWarnings("unchecked") 115 private static <S> Iterable<S> load8(Class<S> service) throws InternalError { 116 try { 117 if (loadMethod == null) { 118 loadMethod = Services.class.getMethod("load", Class.class); 119 } 120 return (Iterable<S>) loadMethod.invoke(null, service); 121 } catch (Exception e) { 122 throw new InternalError(e); 123 } 124 } 125 126 /** 127 * Gets the provider for a given service for which at most one provider must be available. 128 * 129 * @param service the service whose provider is being requested 130 * @param required specifies if an {@link InternalError} should be thrown if no provider of 131 * {@code service} is available 132 * @return the requested provider if available else {@code null} 133 * @throws SecurityException if on JDK8 and a security manager is present and it denies 134 * {@link JVMCIPermission} 135 */ 136 public static <S> S loadSingle(Class<S> service, boolean required) { 137 assert !service.getName().startsWith("jdk.vm.ci") : "JVMCI services must be loaded via " + Services.class.getName(); 138 Iterable<S> providers = load(service); 139 S singleProvider = null; 140 try { 141 for (Iterator<S> it = providers.iterator(); it.hasNext();) { 142 singleProvider = it.next(); 143 if (it.hasNext()) { 144 S other = it.next(); 145 throw new InternalError(String.format("Multiple %s providers found: %s, %s", service.getName(), singleProvider.getClass().getName(), other.getClass().getName())); 146 } 147 } 148 } catch (ServiceConfigurationError e) { 149 // If the service is required we will bail out below. 150 } 151 if (singleProvider == null) { 152 if (required) { 153 throw new InternalError(String.format("No provider for %s found", service.getName())); 154 } 155 } 156 return singleProvider; 157 } 158} 159