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 jdk.internal.nicl;
  24 
  25 import jdk.internal.misc.JavaLangAccess;
  26 import jdk.internal.misc.SharedSecrets;
  27 import jdk.internal.org.objectweb.asm.Type;
  28 
  29 import java.lang.invoke.MethodHandles.Lookup;
  30 import java.nicl.Library;
  31 import java.nicl.Libraries;
  32 import java.nicl.metadata.NativeHeader;
  33 import java.nicl.metadata.NativeStruct;
  34 import java.nio.file.Files;
  35 import java.nio.file.Path;
  36 import java.nio.file.Paths;
  37 import java.util.Arrays;
  38 import java.util.Optional;
  39 import java.security.AccessController;
  40 import java.security.PrivilegedAction;
  41 
  42 public final class LibrariesHelper {
  43     private LibrariesHelper() {}
  44 
  45     private static final JavaLangAccess jlAccess = SharedSecrets.getJavaLangAccess();
  46 
  47     private static String generateImplName(Class<?> c) {
  48         return Type.getInternalName(c) + "$" + "Impl";
  49     }
  50 
  51     /**
  52      * Generate the implementation for an interface.
  53      *
  54      * @param c the interface for which to return an implementation class
  55      * @param generator a generator capable of generating an implementation, if needed
  56      * @return a class implementing the interface
  57      */
  58     private static Class<?> generateImpl(Class<?> c, BinderClassGenerator generator) {
  59         try {
  60             return AccessController.doPrivileged((PrivilegedAction<Class<?>>) () -> {
  61                     return generator.generate();
  62                 });
  63         } catch (Exception e) {
  64             throw new RuntimeException("Failed to generate implementation for class " + c, e);
  65         }
  66     }
  67 
  68     // Cache: Struct interface Class -> Impl Class.
  69     private static final ClassValue<Class<?>> STRUCT_IMPLEMENTATIONS = new ClassValue<>() {
  70         @Override
  71         protected Class<?> computeValue(Class<?> c) {
  72             assert c.isAnnotationPresent(NativeStruct.class);
  73             return generateImpl(c, new StructImplGenerator(c, generateImplName(c), c));
  74         }
  75     };
  76 
  77     /**
  78      * Get the implementation for a Struct interface.
  79      *
  80      * @param c the Struct interface for which to return an implementation class
  81      * @return a class implementing the interface
  82      */
  83     @SuppressWarnings("unchecked")
  84     public static <T> Class<? extends T> getStructImplClass(Class<T> c) {
  85         if (!c.isAnnotationPresent(NativeStruct.class)) {
  86             throw new IllegalArgumentException("Not a Struct interface: " + c);
  87         }
  88 
  89         return (Class<? extends T>)STRUCT_IMPLEMENTATIONS.get(c);
  90     }
  91 
  92     // This is used to pass the current SymbolLookup object to the header computeValue method below.
  93     private static final ThreadLocal<SymbolLookup> curSymLookup = new ThreadLocal<>();
  94 
  95     // Cache: Header interface Class -> Impl Class.
  96     private static final ClassValue<Class<?>> HEADER_IMPLEMENTATIONS = new ClassValue<>() {
  97         @Override
  98         protected Class<?> computeValue(Class<?> c) {
  99             assert c.isAnnotationPresent(NativeHeader.class);
 100             assert curSymLookup.get() != null;
 101             String implName = generateImplName(c);
 102             BinderClassGenerator generator = new HeaderImplGenerator(c, implName, c, curSymLookup.get());
 103             return generateImpl(c, generator);
 104         }
 105     };
 106 
 107     /**
 108      * Get an implementation class for a header type
 109      *
 110      * @param c an interface representing a header file - must have an @NativeHeader annotation
 111      * @param lookup the symbol lookup to use to look up native symbols
 112      * @return a class implementing the header
 113      */
 114     @SuppressWarnings("unchecked")
 115     private static <T> Class<? extends T> getHeaderImplClass(Class<T> c, SymbolLookup lookup) {
 116         if (!c.isAnnotationPresent(NativeHeader.class)) {
 117             throw new IllegalArgumentException("No @NativeHeader annotation on class " + c);
 118         }
 119 
 120         // Thread local is used to pass additional argument to the header
 121         // implementation generator's computeValue method.
 122         try {
 123             curSymLookup.set(lookup);
 124             return (Class<? extends T>)HEADER_IMPLEMENTATIONS.get(c);
 125         } finally {
 126             curSymLookup.remove();
 127         }
 128     }
 129 
 130     /**
 131      * Load the specified shared library.
 132      *
 133      * @param lookup Lookup object of the caller.
 134      * @param name Name of the shared library to load.
 135      */
 136     public static Library loadLibrary(Lookup lookup, String name) {
 137         return jlAccess.loadLibrary(lookup, name);
 138     }
 139 
 140     /**
 141      * Load the specified shared library.
 142      *
 143      * @param lookup Lookup object of the caller.
 144      * @param path Path of the shared library to load.
 145      */
 146     public static Library load(Lookup lookup, String path) {
 147         return jlAccess.load(lookup, path);
 148     }
 149 
 150     // return the absolute path of the library of given name by searching
 151     // in the given array of paths.
 152     private static Optional<Path> findLibraryPath(Path[] paths, String libName) {
 153          return Arrays.stream(paths).
 154               map(p -> p.resolve(System.mapLibraryName(libName))).
 155               filter(Files::isRegularFile).map(Path::toAbsolutePath).findFirst();
 156     }
 157 
 158     /**
 159      * Load the specified shared libraries from the specified paths.
 160      *
 161      * @param lookup Lookup object of the caller.
 162      * @param pathStrs array of paths to load the shared libraries from.
 163      * @param names array of shared library names.
 164      */
 165     // used by jextract tool to load libraries for symbol checks.
 166     public static Library[] loadLibraries(Lookup lookup, String[] pathStrs, String[] names) {
 167         if (pathStrs == null || pathStrs.length == 0) {
 168             return Arrays.stream(names).map(
 169                 name -> Libraries.loadLibrary(lookup, name)).toArray(Library[]::new);
 170         } else {
 171             Path[] paths = Arrays.stream(pathStrs).map(Paths::get).toArray(Path[]::new);
 172             return Arrays.stream(names).map(libName -> {
 173                 Optional<Path> absPath = findLibraryPath(paths, libName);
 174                 return absPath.isPresent() ?
 175                     Libraries.load(lookup, absPath.get().toString()) :
 176                     Libraries.loadLibrary(lookup, libName);
 177             }).toArray(Library[]::new);
 178         }
 179     }
 180 
 181     private static Library[] loadLibraries(Lookup lookup, NativeHeader nativeHeader) {
 182         return loadLibraries(lookup, nativeHeader.libraryPaths(), nativeHeader.libraries());
 183     }
 184 
 185     private static SymbolLookup getSymbolLookupForClass(Lookup lookup, Class<?> c) {
 186         NativeHeader nativeHeader = c.getAnnotation(NativeHeader.class);
 187         Library[] libs = nativeHeader == null || nativeHeader.libraries().length == 0 ?
 188             new Library[] { getDefaultLibrary() } :
 189             loadLibraries(lookup, nativeHeader);
 190 
 191         return new SymbolLookup(libs);
 192     }
 193 
 194     public static Library getDefaultLibrary() {
 195         return jlAccess.defaultLibrary();
 196     }
 197 
 198     /**
 199      * Create a raw, uncivilized version of the interface
 200      *
 201      * @param c the interface class to bind
 202      * @param lib the library in which to look for native symbols
 203      * @return an object of class implementing the interfacce
 204      */
 205     public static <T> T bind(Class<T> c, Library lib) {
 206         return bind(c, new SymbolLookup(lib));
 207     }
 208 
 209     private static <T> T bind(Class<T> c, SymbolLookup lookup) {
 210         Class<? extends T> cls = getHeaderImplClass(c, lookup);
 211 
 212         try {
 213             @SuppressWarnings("unchecked")
 214             T instance = (T) cls.getDeclaredConstructor().newInstance();
 215             return instance;
 216         } catch (ReflectiveOperationException e) {
 217             throw new Error(e);
 218         }
 219     }
 220 
 221     /**
 222      * Create a raw, uncivilized version of the interface
 223      *
 224      * @param lookup the lookup object (used for implicit native library lookup)
 225      * @param c the class to bind
 226      * @return an object of class implementing the interfacce
 227      */
 228     public static <T> T bind(Lookup lookup, Class<T> c) {
 229         return bind(c, getSymbolLookupForClass(lookup, c));
 230     }
 231 }