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.Unsafe;
  26 import jdk.internal.org.objectweb.asm.Type;
  27 
  28 import java.lang.invoke.MethodHandle;
  29 import java.lang.invoke.MethodType;
  30 import java.io.File;
  31 import java.nicl.Library;
  32 import java.nicl.NativeLibrary;
  33 import java.nicl.metadata.LibraryDependencies;

  34 import java.nicl.metadata.NativeType;
  35 import java.nio.file.Files;
  36 import java.nio.file.Path;
  37 import java.nio.file.Paths;
  38 import java.util.Arrays;
  39 import java.util.Map;
  40 import java.util.WeakHashMap;
  41 import java.util.Optional;
  42 import java.security.AccessController;
  43 import java.security.PrivilegedAction;
  44 
  45 public class NativeLibraryImpl {
  46     enum ImplType {
  47         HEADER, CIVILIZED;
  48 
  49         public String getImplName() {
  50             return name().charAt(0) + name().substring(1).toLowerCase();
  51         }
  52     }
  53 
  54     // Map of interface -> impl class (per ImplType)
  55     @SuppressWarnings("unchecked")
  56     private static final Map<Class<?>, Class<?>>[] IMPLEMENTATIONS = (Map<Class<?>, Class<?>>[]) new Map<?, ?>[ImplType.values().length];
  57 
  58     static {
  59         for (int i = 0; i < IMPLEMENTATIONS.length; i++) {
  60             IMPLEMENTATIONS[i] = new WeakHashMap<>();
  61         }
  62     }
  63 
  64     private static final Unsafe U = Unsafe.getUnsafe();
  65 
  66     private static String generateImplName(Class<?> c, ImplType type) {
  67         return Type.getInternalName(c) + "$" + type.getImplName() + "Impl";
  68     }
  69 
  70     public static <T> Class<? extends T> getImplClass(Class<T> c) {
  71         return getOrCreateImpl(c, getSymbolLookupForClass(c));
  72     }
  73 
  74     /**
  75      * Look up the implementation for an interface, or generate it if needed
  76      *
  77      * @param c the interface for which to return an implementation class
  78      * @param generator a generator capable of generating an implementation, if needed
  79      * @return a class implementing the interface
  80      */
  81     @SuppressWarnings("unchecked")
  82     private static <T> Class<? extends T> getOrCreateImpl(ImplType type, Class<T> c, ImplGenerator generator) {
  83         Class<? extends T> implCls;
  84 
  85         Map<Class<?>, Class<?>> map = IMPLEMENTATIONS[type.ordinal()];
  86 
  87         synchronized (map) {
  88             implCls = (Class<? extends T>) map.get(c);
  89         }
  90 
  91         if (implCls == null) {
  92             Class<? extends T> newCls;
  93             try {
  94                 newCls = (Class<? extends T>)AccessController.doPrivileged((PrivilegedAction<Class<?>>) () -> {
  95                         return generator.generate();
  96                     });
  97             } catch (Exception e) {
  98                 throw new RuntimeException("Failed to generate " + type + " for class " + c, e);
  99             }
 100 
 101             synchronized (map) {
 102                 implCls = (Class<? extends T>) map.get(c);
 103                 if (implCls == null) {
 104                     map.put(c, newCls);
 105                     implCls = newCls;
 106                 }
 107             }
 108         }
 109 
 110         return implCls;
 111     }
 112 
 113     /**
 114      * Generate an implementation class for a header type
 115      *
 116      * @param c an interface representing a header file - must have an @Header annotation
 117      * @param lookup the symbol lookup to use to look up native symbols
 118      * @return a class implementing the header
 119      */
 120     private static <T> Class<? extends T> getOrCreateImpl(Class<T> c, SymbolLookup lookup)
 121             throws SecurityException, InternalError {
 122         /*
 123         if (!c.isAnnotationPresent(Header.class)) {
 124             throw new IllegalArgumentException("No @Header annotation on class " + c);
 125         }
 126         */
 127 
 128         String implClassName = generateImplName(c, ImplType.HEADER);
 129 
 130         boolean isRecordType = c.isAnnotationPresent(NativeType.class) && c.getAnnotation(NativeType.class).isRecordType();
 131 
 132         return getOrCreateImpl(ImplType.HEADER, c, new HeaderImplGenerator(c, implClassName, c, lookup, isRecordType));
 133     }
 134 
 135     private static <T> Class<? extends T> getOrCreateCivilizedImpl(Class<T> c, T rawInstance)
 136             throws SecurityException, InternalError {
 137         /*
 138         if (!c.isAnnotationPresent(Header.class)) {
 139             throw new IllegalArgumentException("No @Header annotation on class " + c);
 140         }
 141         */
 142 
 143         String implClassName = generateImplName(c, ImplType.CIVILIZED);
 144 
 145         return getOrCreateImpl(ImplType.CIVILIZED, c, new CivilizedHeaderImplGenerator<>(c, implClassName, c, rawInstance));
 146     }
 147 
 148     private static Library loadLibrary(String name, boolean isAbsolute) {
 149         return Platform.getInstance().loadLibrary(name, isAbsolute);
 150     }
 151 
 152     public static Library loadLibrary(String name) {
 153         assert name.indexOf(File.separatorChar) == -1;
 154         return loadLibrary(name, false);
 155     }
 156 
 157     public static Library load(String path) {
 158         assert new File(path).isAbsolute();
 159         return loadLibrary(path, true);
 160     }
 161 
 162     // return the absolute path of the library of given name by searching
 163     // in the given array of paths.
 164     private static Optional<Path> findLibraryPath(Path[] paths, String libName) {
 165          return Arrays.stream(paths).
 166               map(p -> p.resolve(System.mapLibraryName(libName))).
 167               filter(Files::isRegularFile).map(Path::toAbsolutePath).findFirst();
 168     }
 169 
 170     private static Library[] loadLibraries(LibraryDependencies deps) {
 171         String[] pathStrs = deps.paths();
 172         if (pathStrs == null || pathStrs.length == 0) {
 173             return Arrays.stream(deps.names()).map(
 174                 NativeLibrary::loadLibrary).toArray(Library[]::new);
 175         } else {
 176             Path[] paths = Arrays.stream(pathStrs).map(Paths::get).toArray(Path[]::new);
 177             return Arrays.stream(deps.names()).map(libName -> {
 178                 Optional<Path> absPath = findLibraryPath(paths, libName);
 179                 return absPath.isPresent() ?
 180                     NativeLibrary.load(absPath.get().toString()) :
 181                     NativeLibrary.loadLibrary(libName);
 182             }).toArray(Library[]::new);
 183         }
 184     }
 185 
 186     private static LibraryDependencies getLibraryDependenciesForClass(Class<?> c) {
 187         if (c.isAnnotationPresent(LibraryDependencies.class)) {
 188             return c.getAnnotation(LibraryDependencies.class);


 189         } else {
 190             return null;
 191         }
 192     }
 193 
 194     private static SymbolLookup getSymbolLookupForClass(Class<?> c) {
 195         LibraryDependencies deps = getLibraryDependenciesForClass(c);
 196 
 197         Library[] libs;
 198 
 199         if (deps == null) {
 200             // FIXME: Require @LibraryDependencies on all relevant classes
 201             // System.err.println("WARNING: No @LibraryDependencies annotation on class " + c.getName());
 202             // throw new IllegalArgumentException("No @LibraryDependencies annotation on class " + c.getName());
 203             libs = new Library[] { getDefaultLibrary() };
 204         } else {
 205             libs = loadLibraries(deps);
 206         }
 207 
 208         return new SymbolLookup(libs);
 209     }
 210 
 211     public static Library getDefaultLibrary() {
 212         return Platform.getInstance().defaultLibrary();
 213     }
 214 
 215     @Deprecated
 216     public static <T> T bindRaw(Class<T> c, Library lib) {
 217         return bindRaw(c, new SymbolLookup(new Library[] { lib }));
 218     }
 219 
 220     private static <T> T bindRaw(Class<T> c, SymbolLookup lookup) {
 221         Class<? extends T> cls = getOrCreateImpl(c, lookup);
 222 
 223         try {
 224             //FIXME: Run some constructor here...?
 225             @SuppressWarnings("unchecked")
 226             T instance = (T) U.allocateInstance(cls);
 227             return instance;
 228         } catch (ReflectiveOperationException e) {
 229             throw new Error(e);
 230         }
 231     }
 232 
 233     public static <T> T bindRaw(Class<T> c) {
 234         return bindRaw(c, getSymbolLookupForClass(c));
 235     }
 236 
 237     private static <T> Object bind(Class<T> c, SymbolLookup lookup) {
 238         try {
 239             T rawInstance = bindRaw(c);
 240 
 241             Class<?> civilizedCls = NativeLibraryImpl.getOrCreateCivilizedImpl(c, rawInstance);
 242 
 243             return U.allocateInstance(civilizedCls);
 244         } catch (Throwable t) {
 245             throw new RuntimeException(t);
 246         }
 247     }
 248 
 249     @Deprecated
 250     public static <T> Object bind(Class<T> c, Library lib) {
 251         return bind(c, new SymbolLookup(lib));
 252     }
 253 
 254     public static <T> Object bind(Class<T> c) {
 255         return bind(c, getSymbolLookupForClass(c));
 256     }
 257 
 258     public static MethodHandle lookupNativeMethod(Library[] libs, String symbolName, MethodType methodType, boolean isVarArgs) throws NoSuchMethodException, IllegalAccessException {
 259         NativeInvoker invoker = new NativeInvoker(methodType, isVarArgs, new SymbolLookup(libs), symbolName);
 260         return invoker.getBoundMethodHandle().asCollector(Object[].class, methodType.parameterCount());
 261     }
 262 }
--- EOF ---