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.findLibrary(lookup, name);
138 }
139
140 // return the absolute path of the library of given name by searching
141 // in the given array of paths.
142 private static Optional<Path> findLibraryPath(Path[] paths, String libName) {
143 return Arrays.stream(paths).
144 map(p -> p.resolve(System.mapLibraryName(libName))).
145 filter(Files::isRegularFile).map(Path::toAbsolutePath).findFirst();
146 }
147
148 /**
149 * Load the specified shared libraries from the specified paths.
150 *
151 * @param lookup Lookup object of the caller.
152 * @param pathStrs array of paths to load the shared libraries from.
153 * @param names array of shared library names.
154 */
155 // used by jextract tool to load libraries for symbol checks.
156 public static Library[] loadLibraries(Lookup lookup, String[] pathStrs, String[] names) {
157 if (pathStrs == null || pathStrs.length == 0) {
158 return Arrays.stream(names).map(
159 name -> Libraries.loadLibrary(lookup, name)).toArray(Library[]::new);
160 } else {
161 Path[] paths = Arrays.stream(pathStrs).map(Paths::get).toArray(Path[]::new);
162 return Arrays.stream(names).map(libName -> {
163 Optional<Path> absPath = findLibraryPath(paths, libName);
164 return absPath.isPresent() ?
165 Libraries.load(lookup, absPath.get().toString()) :
166 Libraries.loadLibrary(lookup, libName);
167 }).toArray(Library[]::new);
168 }
169 }
170
171 private static Library[] loadLibraries(Lookup lookup, NativeHeader nativeHeader) {
172 return loadLibraries(lookup, nativeHeader.libraryPaths(), nativeHeader.libraries());
173 }
174
175 private static SymbolLookup getSymbolLookupForClass(Lookup lookup, Class<?> c) {
176 NativeHeader nativeHeader = c.getAnnotation(NativeHeader.class);
177 Library[] libs = nativeHeader == null || nativeHeader.libraries().length == 0 ?
178 new Library[] { getDefaultLibrary() } :
179 loadLibraries(lookup, nativeHeader);
180
181 return new SymbolLookup(libs);
182 }
183
184 public static Library getDefaultLibrary() {
185 return jlAccess.defaultLibrary();
186 }
187
188 /**
189 * Create a raw, uncivilized version of the interface
190 *
191 * @param c the interface class to bind
192 * @param lib the library in which to look for native symbols
193 * @return an object of class implementing the interfacce
194 */
195 public static <T> T bind(Class<T> c, Library lib) {
196 return bind(c, new SymbolLookup(lib));
197 }
198
199 private static <T> T bind(Class<T> c, SymbolLookup lookup) {
200 Class<? extends T> cls = getHeaderImplClass(c, lookup);
201
202 try {
203 @SuppressWarnings("unchecked")
204 T instance = (T) cls.getDeclaredConstructor().newInstance();
205 return instance;
206 } catch (ReflectiveOperationException e) {
207 throw new Error(e);
208 }
209 }
210
211 /**
212 * Create a raw, uncivilized version of the interface
213 *
214 * @param lookup the lookup object (used for implicit native library lookup)
215 * @param c the class to bind
216 * @return an object of class implementing the interfacce
217 */
218 public static <T> T bind(Lookup lookup, Class<T> c) {
219 return bind(c, getSymbolLookupForClass(lookup, c));
220 }
221 }
--- EOF ---