From 5d8243ec3b33095882dcfd3b3eac36118120da7e Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Sun, 21 Apr 2013 09:33:05 -0400 Subject: [PATCH 01/15] revise resource extraction to use explicit class loaders --- CHANGES.md | 1 + TODO | 70 ++++++++++-------- build.xml | 12 ++- src/com/sun/jna/Library.java | 9 +++ src/com/sun/jna/Native.java | 97 +++++++++++-------------- src/com/sun/jna/NativeLibrary.java | 40 ++++++++-- src/com/sun/jna/Platform.java | 6 +- src/com/sun/jna/Structure.java | 2 +- test/com/sun/jna/DirectTest.java | 13 +--- test/com/sun/jna/JNAUnloadTest.java | 10 +-- test/com/sun/jna/LibraryLoadTest.java | 71 ++++++++---------- test/com/sun/jna/NativeLibraryTest.java | 25 +++++++ test/com/sun/jna/NativeTest.java | 56 -------------- test/com/sun/jna/Paths.java | 25 +++++++ test/com/sun/jna/PerformanceTest.java | 13 +--- test/com/sun/jna/PlatformTest.java | 80 ++++++++++++++++++++ test/com/sun/jna/WebStartTest.java | 9 +-- 17 files changed, 310 insertions(+), 229 deletions(-) create mode 100644 test/com/sun/jna/Paths.java create mode 100644 test/com/sun/jna/PlatformTest.java diff --git a/CHANGES.md b/CHANGES.md index a9196c7ab4..93ca4ce036 100755 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ Features * [#209](https://github.com/twall/jna/issues/209): Improve default performance saving last error results - [@twall](https://github.com/twall). * Use predictable names for CPU arch prefix (namely x86, x86-64); names correspond to OSGI processor values - [@twall](https://github.com/twall). * Avoid superfluous Structure memory allocation from native - [@twall](https://github.com/twall). +* Added `Library.OPTION_CLASSLOADER`, which enables loading native libraries from any class loader (including JNA's native library). This enables parallel dependencies on JNA (e.g. within a tomcat deployment without having to include JNA in the app server environment) - [@twall](https://github.com/twall). Bug Fixes --------- diff --git a/TODO b/TODO index afa42bee4f..452a4c6c50 100644 --- a/TODO +++ b/TODO @@ -10,21 +10,51 @@ o arrays are treated as pointers, except within a struct - in structure (inline; otherwise pointer-to-X should be used) - as function argument (auto-convert to pointer via Memory) - o pointer to type in struct should use Pointer or ByReference + o pointer to type in struct should use PointerType or Structure.ByReference o "free" should be invisible wherever possible # FUTURE DEMOS: -* test keyboard keys' state (needs OSX) +* test keyboard keys' state (needs OSX impl) * get system process information, basic process control * additional file utilities (meta info, free space, etc) * enforce window minimum/maximum size (workaround java bug) # TODO -# DOCS: how to properly use W32API_DEFAULT_OPTIONS (A/W, String/WString) +* annotations/generics: + o GetPrimitiveArrayCritical: use this if flagged (by annotation? method name?) + o per-arg/return type marshalling + o avoid casts in loadLibrary + o wrap global variables w/type (set/get) + o support annotations of parameters and return values w/o breaking + 1.4 compatibility (retroweaver?), e.g. + -- retroweaver (requires retroweaver runtime classes to provide annotation + features) not yet worth doing -* make native dll extraction from jar public, to use with user dlls packaged - in a jar + void my_function(@MarshalAs(off_t)long arg); + + trading off cruft in the interface def to avoid cruft in the usage: + + my_function(0) versus my_function(new off_t(0)) + + this only really applies to NativeLong/IntegerType types that want to use a + primitive value instead of creating an object instance. + o flag string(/wstring)-returning methods which need to free their result + to avoid leaking memory whan auto-creating strings from the result + (since normally we don't take explicit ownership of any returned pointers) + this is a special case for returned strings only, since other returned + pointers are available to the user for later "freeing" + Use a map on library creation, an iface for the library, or annotation + NOTE: how many methods actually do this? strdup... + Maybe make function return "Memory", which can then be converted to a + Structure or some other type. Then memory will free itself when no longer + referenced. (not really common) + + + +* auto-generate direct mappings/bindings on a per-method basis (perform a + method register on first call to an interface-mapped function) with + sufficient pre-processing to convert to primitives. * dispose memory/callbacks in Native finalizer to ensure they run first (use referencequeue to run them when they become unreachable) @@ -34,39 +64,13 @@ and forth multiple times). This also makes it easer to perform conversions (no native changes required). -* GetPrimitiveArrayCritical: use this if flagged (by annotation? method name?) * direct/raw non-primitive array arguments (String[], Pointer[], NativeMapped[]) * ppc64 direct/raw failures (multiple) * direct calls on ppc to varargs (callbacks) with FP args fail; avoid them for now -* combine direct and interface mapping calling code where possible * Callback.PostCallWrite.write() cf PostCallRead * eliminate type conversion contexts; these are almost entirely unused -* support annotations of parameters and return values w/o breaking - 1.4 compatibility (retroweaver?), e.g. - -- retroweaver (requires retroweaver runtime classes to provide annotation - features) not yet worth doing - - void my_function(@MarshalAs(off_t)long arg); - - trading off cruft in the interface def to avoid cruft in the usage: - - my_function(0) versus my_function(new off_t(0)) - - this only really applies to NativeLong/IntegerType types that want to use a - primitive value instead of creating an object instance. - -* flag string(/wstring)-returning methods which need to free their result - to avoid leaking memory whan auto-creating strings from the result - (since normally we don't take explicit ownership of any returned pointers) - this is a special case for returned strings only, since other returned - pointers are available to the user for later "freeing" - Use a map on library creation, an iface for the library, or annotation - NOTE: how many methods actually do this? strdup... - Maybe make function return "Memory", which can then be converted to a - Structure or some other type. Then memory will free itself when no longer - referenced. * universal GCC build w/cross-compile (needs cross compilers...) * return Pointer.SIZE/LONG_SIZE/WCHAR_SIZE in bits (for consistency with 1.5) Long.SIZE, Integer.SIZE, et al. @@ -76,6 +80,10 @@ # DONE +* combine direct and interface mapping calling code where possible +* DOCS: how to properly use W32API_DEFAULT_OPTIONS (A/W, String/WString) +* make native dll extraction from jar public, to use with user dlls packaged + in a jar * use consistent cpu references (x86->i386, x86_64->amd64) * osgi for osx * NO: make Pointer free-able (system allocates memory, client frees it); actually, diff --git a/build.xml b/build.xml index 198625c422..0d8c4c4627 100644 --- a/build.xml +++ b/build.xml @@ -725,13 +725,9 @@ osname=macosx;processor=x86;processor=x86-64;processor=ppc - - + - - - - + @@ -741,7 +737,7 @@ osname=macosx;processor=x86;processor=x86-64;processor=ppc - + @@ -781,6 +777,8 @@ osname=macosx;processor=x86;processor=x86-64;processor=ppc + + diff --git a/src/com/sun/jna/Library.java b/src/com/sun/jna/Library.java index 7ca76c83b4..5d87da184c 100644 --- a/src/com/sun/jna/Library.java +++ b/src/com/sun/jna/Library.java @@ -83,6 +83,15 @@ public interface Library { String OPTION_CALLING_CONVENTION = "calling-convention"; /** Flags to use when opening the native library (see {@link Native#open(String,int)}) */ String OPTION_OPEN_FLAGS = "open-flags"; + /** Class loader to use when searching for native libraries on the + * resource path (classpath). If not provided the current thread's + * context class loader is used.

+ * If extracted from the resource path (i.e. bundled in a jar file), the + * loaded library's lifespan will mirror that of the class loader, which + * means you can use the same library in isolated contexts without + * conflict. + */ + String OPTION_CLASSLOADER = "classloader"; static class Handler implements InvocationHandler { diff --git a/src/com/sun/jna/Native.java b/src/com/sun/jna/Native.java index 6fbc0e193b..4f2a021936 100644 --- a/src/com/sun/jna/Native.java +++ b/src/com/sun/jna/Native.java @@ -32,11 +32,13 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLClassLoader; import java.nio.Buffer; import java.nio.ByteBuffer; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -601,23 +603,6 @@ public static char[] toCharArray(String s) { return buf; } - /** Generate a canonical String prefix based on the current OS - type/arch/name. - */ - public static String getNativeLibraryResourcePrefix() { - return getNativeLibraryResourcePrefix(Platform.getOSType(), System.getProperty("os.arch"), System.getProperty("os.name")); - } - - /** Generate a canonical String prefix based on the given OS - type/arch/name. - @param osType from {@link Platform} - @param arch from os.arch System property - @param name from os.name System property - */ - public static String getNativeLibraryResourcePrefix(int osType, String arch, String name) { - return Platform.getNativeLibraryResourcePrefix(osType, arch, name); - } - /** * Loads the JNA stub library. * First tries jna.boot.library.path, then the system path, then from the @@ -699,8 +684,8 @@ private static void loadNativeDispatchLibrary() { */ private static void loadNativeDispatchLibraryFromClasspath() { try { - String prefix = "/com/sun/jna/" + getNativeLibraryResourcePrefix(); - File lib = extractFromResourcePath("jnidispatch", prefix, Native.class.getClassLoader()); + String libName = "/com/sun/jna/" + Platform.RESOURCE_PREFIX + "/" + System.mapLibraryName("jnidispatch"); + File lib = extractFromResourcePath(libName, Native.class.getClassLoader()); if (lib == null) { throw new UnsatisfiedLinkError("Could not find JNA native support"); } @@ -724,50 +709,52 @@ static boolean isUnpacked(File file) { return file.getName().startsWith(JNA_TMPLIB_PREFIX); } - /** Attempt to extract a native library from the current resource path. - * Expects native libraries to be stored under - * the path returned by {@link #getNativeLibraryResourcePrefix()}, - * and reachable by the current thread context class loader. - * @param name Base name of native library to extract + /** Attempt to extract a native library from the current resource path, + * using the current thread context class loader. + * @param name Base name of native library to extract. May also be an + * absolute resource path (i.e. starts with "/"), in which case the + * no transformations of the library name are performed. If only the base + * name is given, the resource path is attempted both with and without + * {@link Platform#RESOURCE_PREFIX}, after mapping the library name via + * {@link NativeLibrary#mapSharedLibraryName()}. * @return File indicating extracted resource on disk * @throws IOException if resource not found */ - static File extractFromResourcePath(String name) throws IOException { - return extractFromResourcePath(name, "/" + getNativeLibraryResourcePrefix(), Thread.currentThread().getContextClassLoader()); - } - - /** Attempt to extract a native library from the current resource path. - * Expects native libraries to be stored under - * the path returned by {@link #getNativeLibraryResourcePrefix(int, String, - * String)}. - * @param name Base name of native library to extract + public static File extractFromResourcePath(String name) throws IOException { + return extractFromResourcePath(name, null); + } + + /** Attempt to extract a native library from the resource path using the + * given class loader. + * @param name Base name of native library to extract. May also be an + * absolute resource path (i.e. starts with "/"), in which case the + * no transformations of the library name are performed. If only the base + * name is given, the resource path is attempted both with and without + * {@link Platform#RESOURCE_PREFIX}, after mapping the library name via + * {@link NativeLibrary#mapSharedLibraryName()}. * @param loader Class loader to use to load resources - * @param resourcePrefix prefix to use when looking for the resource * @return File indicating extracted resource on disk * @throws IOException if resource not found */ - static File extractFromResourcePath(String name, String resourcePrefix, ClassLoader loader) throws IOException { - String libname = name.startsWith("/") ? name : System.mapLibraryName(name); - String resourcePath = name.startsWith("/") ? name : resourcePrefix + "/" + libname; + public static File extractFromResourcePath(String name, ClassLoader loader) throws IOException { + if (loader == null) { + loader = Thread.currentThread().getContextClassLoader(); + } + String libname = name.startsWith("/") ? name : NativeLibrary.mapSharedLibraryName(name); + String resourcePath = name.startsWith("/") ? name : Platform.RESOURCE_PREFIX + "/" + libname; if (resourcePath.startsWith("/")) { resourcePath = resourcePath.substring(1); } URL url = loader.getResource(resourcePath); - - // User libraries will have '.dylib' - if (url == null && Platform.isMac()) { - if (resourcePath.endsWith(".jnilib")) { - resourcePath = resourcePath.substring(0, resourcePath.lastIndexOf(".jnilib")) + ".dylib"; - } - // Ugly hack for OpenJDK (soylatte) - JNI libs use the usual - // .dylib extension - else if (resourcePath.endsWith(".dylib")) { - resourcePath = resourcePath.substring(0, resourcePath.lastIndexOf(".dylib")) + ".jnilib"; - } - url = loader.getResource(resourcePath); + if (url == null && resourcePath.startsWith(Platform.RESOURCE_PREFIX)) { + url = loader.getResource(libname); } if (url == null) { - throw new IOException("Native library (" + resourcePath + ") not found in resource path (" + System.getProperty("java.class.path") + ")"); + String path = System.getProperty("java.class.path"); + if (loader instanceof URLClassLoader) { + path = Arrays.asList(((URLClassLoader)loader).getURLs()).toString(); + } + throw new IOException("Native library (" + resourcePath + ") not found in resource path (" + path + ")"); } File lib = null; @@ -1298,13 +1285,17 @@ private static int getConversion(Class type, TypeMapper mapper) { /** When called from a class static initializer, maps all native methods * found within that class to native libraries via the JNA raw calling - * interface. + * interface. Uses the class loader of the given class to search for the + * native library in the resource path if it is not found in the system + * library load path or jna.library.path. * @param cls Class with native methods to register * @param libName name of or path to native library to which functions * should be bound */ public static void register(Class cls, String libName) { - register(cls, NativeLibrary.getInstance(libName)); + Map options = new HashMap(); + options.put(Library.OPTION_CLASSLOADER, cls.getClassLoader()); + register(cls, NativeLibrary.getInstance(libName, options)); } /** When called from a class static initializer, maps all native methods @@ -1548,7 +1539,7 @@ public static void main(String[] args) { System.out.println("Version: " + version); System.out.println(" Native: " + getNativeVersion() + " (" + getAPIChecksum() + ")"); - System.out.println(" Prefix: " + getNativeLibraryResourcePrefix()); + System.out.println(" Prefix: " + Platform.RESOURCE_PREFIX); } /** Free the given callback trampoline. */ diff --git a/src/com/sun/jna/NativeLibrary.java b/src/com/sun/jna/NativeLibrary.java index 2abd0073c3..c03f7a4d16 100644 --- a/src/com/sun/jna/NativeLibrary.java +++ b/src/com/sun/jna/NativeLibrary.java @@ -1,5 +1,5 @@ /* Copyright (c) 2007 Wayne Meissner, All Rights Reserved - * Copyright (c) 2007, 2008, 2009 Timothy Wall, All Rights Reserved + * Copyright (c) 2007-20013 Timothy Wall, All Rights Reserved * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -54,7 +54,7 @@ * installed on the classpath under * ${os-prefix}/LIBRARY_FILENAME, where ${os-prefix} * is the OS/Arch prefix returned by {@link - * Native#getNativeLibraryResourcePrefix()}. If bundled in a jar file, the + * Platform#getNativeLibraryResourcePrefix()}. If bundled in a jar file, the * resource will be extracted to jna.tmpdir for loading, and * later removed (but only if jna.nounpack is false or not set). * @@ -206,7 +206,7 @@ else if (Platform.isWindows()) { // path, using the current context class loader. if (handle == 0) { try { - File embedded = Native.extractFromResourcePath(libraryName); + File embedded = Native.extractFromResourcePath(libraryName, (ClassLoader)options.get(Library.OPTION_CLASSLOADER)); handle = Native.open(embedded.getAbsolutePath()); // Don't leave temporary files around if (Native.isUnpacked(embedded)) { @@ -254,7 +254,7 @@ static String matchFramework(String libraryName) { private String getLibraryName(String libraryName) { String simplified = libraryName; final String BASE = "---"; - String template = mapLibraryName(BASE); + String template = mapSharedLibraryName(BASE); int prefixEnd = template.indexOf(BASE); if (prefixEnd > 0 && simplified.startsWith(template.substring(0, prefixEnd))) { simplified = simplified.substring(prefixEnd); @@ -283,6 +283,28 @@ public static final NativeLibrary getInstance(String libraryName) { return getInstance(libraryName, Collections.EMPTY_MAP); } + /** + * Returns an instance of NativeLibrary for the specified name. + * The library is loaded if not already loaded. If already loaded, the + * existing instance is returned.

+ * More than one name may map to the same NativeLibrary instance; only + * a single instance will be provided for any given unique file path. + * + * @param libraryName The library name to load. + * This can be short form (e.g. "c"), + * an explicit version (e.g. "libc.so.6"), or + * the full path to the library (e.g. "/lib/libc.so.6"). + * @param classLoader The class loader to use to load the native library. + * This only affects library loading when the native library is + * included somewhere in the classpath, either bundled in a jar file + * or as a plain file within the classpath. + */ + public static final NativeLibrary getInstance(String libraryName, ClassLoader loader) { + Map map = new HashMap(); + map.put(Library.OPTION_CLASSLOADER, loader); + return getInstance(libraryName, map); + } + /** * Returns an instance of NativeLibrary for the specified name. * The library is loaded if not already loaded. If already loaded, the @@ -548,7 +570,7 @@ private static String findLibraryPath(String libName, List searchPath) { // // Get the system name for the library (e.g. libfoo.so) // - String name = mapLibraryName(libName); + String name = mapSharedLibraryName(libName); // Search in the JNA paths for it for (Iterator it = searchPath.iterator(); it.hasNext(); ) { @@ -575,8 +597,12 @@ private static String findLibraryPath(String libName, List searchPath) { // return name; } - private static String mapLibraryName(String libName) { + /** Similar to {@link System#mapLibraryName}, except that it maps to + standard shared library formats rather than specifically JNI formats. + @param libName base (undecorated) name of library + */ + static String mapSharedLibraryName(String libName) { if (Platform.isMac()) { if (libName.startsWith("lib") && (libName.endsWith(".dylib") @@ -598,7 +624,7 @@ else if (Platform.isLinux()) { return libName; } } - else if (Platform.isAix()) { // can be libx.a, libx.a(shr.o), libx.so + else if (Platform.isAIX()) { // can be libx.a, libx.a(shr.o), libx.so if (libName.startsWith("lib")) { return libName; } diff --git a/src/com/sun/jna/Platform.java b/src/com/sun/jna/Platform.java index 878afa7f79..3367dd0729 100644 --- a/src/com/sun/jna/Platform.java +++ b/src/com/sun/jna/Platform.java @@ -118,9 +118,13 @@ public static final boolean isAndroid() { public static final boolean isLinux() { return osType == LINUX; } - public static final boolean isAix() { + public static final boolean isAIX() { return osType == AIX; } + /** @deprecated */ + public static final boolean isAix() { + return isAIX(); + } public static final boolean isWindowsCE() { return osType == WINDOWSCE; } diff --git a/src/com/sun/jna/Structure.java b/src/com/sun/jna/Structure.java index a17efa1c74..048d41f9c2 100644 --- a/src/com/sun/jna/Structure.java +++ b/src/com/sun/jna/Structure.java @@ -137,7 +137,7 @@ public interface ByReference { } isSPARC || ((isPPC || isARM) && (Platform.isLinux() || Platform.isAndroid())) - || Platform.isAix() + || Platform.isAIX() ? 8 : Native.LONG_SIZE; protected static final int CALCULATE_SIZE = -1; static final Map layoutInfo = new WeakHashMap(); diff --git a/test/com/sun/jna/DirectTest.java b/test/com/sun/jna/DirectTest.java index 2ecbb19548..1891e95cbe 100644 --- a/test/com/sun/jna/DirectTest.java +++ b/test/com/sun/jna/DirectTest.java @@ -22,20 +22,13 @@ import java.net.URLClassLoader; //@SuppressWarnings("unused") -public class DirectTest extends TestCase { - - private static final String BUILDDIR = - System.getProperty("jna.builddir", - "build" + (Platform.is64Bit() ? "-d64" : "")); +public class DirectTest extends TestCase implements Paths { private static class JNI { static { - String path = BUILDDIR + "/native/" + System.mapLibraryName("testlib");; + String path = TESTPATH + NativeLibrary.mapSharedLibraryName("testlib"); if (!new File(path).isAbsolute()) { - path = System.getProperty("user.dir") + "/" + path; - } - if (path.endsWith(".jnilib")) { - path = path.replace(".jnilib", ".dylib"); + path = new File(path).getAbsolutePath(); } System.load(path); } diff --git a/test/com/sun/jna/JNAUnloadTest.java b/test/com/sun/jna/JNAUnloadTest.java index 1c34258f31..75aa85b1df 100644 --- a/test/com/sun/jna/JNAUnloadTest.java +++ b/test/com/sun/jna/JNAUnloadTest.java @@ -24,12 +24,8 @@ /** Test loading and unloading native support from various locations. Note * that no JNI classes are directly referenced in these tests. */ -public class JNAUnloadTest extends TestCase { +public class JNAUnloadTest extends TestCase implements Paths { - private static final String BUILDDIR = - System.getProperty("jna.builddir", "build" - + (Platform.is64Bit() ? "-d64" : "")); - private class TestLoader extends URLClassLoader { public TestLoader(boolean fromJar) throws MalformedURLException { super(new URL[] { @@ -58,7 +54,7 @@ protected Class findClass(String name) throws ClassNotFoundException { } protected void assertJarExists() { - File jar = new File((Platform.isWindowsCE() ? "/Storage Card" : BUILDDIR) + "/jna.jar"); + File jar = new File(JNAJAR); if (!jar.exists()) { throw new Error("Expected JNA jar file at " + jar + " is missing"); } @@ -67,7 +63,7 @@ protected void assertJarExists() { protected void assertLibraryExists() { String osPrefix = Platform.getNativeLibraryResourcePrefix(); String name = System.mapLibraryName("jnidispatch"); - File lib = new File((Platform.isWindowsCE() ? "/Storage Card" : BUILDDIR + "/classes") + "/com/sun/jna/" + osPrefix + "/" + name); + File lib = new File(CLASSES + "/com/sun/jna/" + osPrefix + "/" + name); if (!lib.exists()) { throw new Error("Expected JNA library at " + lib + " is missing"); } diff --git a/test/com/sun/jna/LibraryLoadTest.java b/test/com/sun/jna/LibraryLoadTest.java index 5f4a00a843..2347bf8a1c 100644 --- a/test/com/sun/jna/LibraryLoadTest.java +++ b/test/com/sun/jna/LibraryLoadTest.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2009 Timothy Wall, All Rights Reserved +/* Copyright (c) 2007-20013 Timothy Wall, All Rights Reserved * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,14 +19,19 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; import junit.framework.TestCase; -public class LibraryLoadTest extends TestCase { +public class LibraryLoadTest extends TestCase implements Paths { - private static final String BUILDDIR = - System.getProperty("jna.builddir", "build" - + (Platform.is64Bit() ? "-d64" : "")); + private class TestLoader extends URLClassLoader { + public TestLoader(File path) throws MalformedURLException { + super(new URL[] { path.toURI().toURL() }, null); + } + } public void testLoadJNALibrary() { assertTrue("Pointer size should never be zero", Pointer.SIZE > 0); @@ -52,33 +57,31 @@ public void testLoadAWTAfterJNA() { } } - public interface TestLibrary extends Library { - } - public void testLoadFromJNALibraryPath() { + // Tests are already configured to load from this path NativeLibrary.getInstance("testlib"); } - public void testLoadFromClasspath() { - NativeLibrary.getInstance("testlib-path"); + public void testLoadFromClasspath() throws MalformedURLException { + NativeLibrary.getInstance("testlib-path", new TestLoader(new File(TESTPATH))); } - public void testLoadFromClasspathAbsolute() { - String name = System.mapLibraryName("testlib-path").replace(".jnilib", ".dylib"); - NativeLibrary.getInstance("/" + Platform.RESOURCE_PREFIX + "/" + name); + public void testLoadFromClasspathAbsolute() throws MalformedURLException { + String name = NativeLibrary.mapSharedLibraryName("testlib-path"); + NativeLibrary.getInstance("/" + name, new TestLoader(new File(TESTPATH))); } - public void testLoadFromJar() { - NativeLibrary.getInstance("testlib-jar"); + public void testLoadFromJar() throws MalformedURLException { + NativeLibrary.getInstance("testlib-jar", new TestLoader(new File(TESTJAR))); } - public void testLoadFromJarAbsolute() { - String name = System.mapLibraryName("testlib-jar").replace(".jnilib", ".dylib"); - NativeLibrary.getInstance("/" + Platform.RESOURCE_PREFIX + "/" + name); + public void testLoadFromJarAbsolute() throws MalformedURLException { + String name = NativeLibrary.mapSharedLibraryName("testlib-jar"); + NativeLibrary.getInstance("/" + name, new TestLoader(new File(TESTJAR))); } - public void testLoadExplicitAbsolutePath() { - NativeLibrary.getInstance(new File(BUILDDIR + "/native/testlib-truncated").getAbsolutePath()); + public void testLoadExplicitAbsolutePath() throws MalformedURLException { + NativeLibrary.getInstance(new File(TESTPATH, "testlib-truncated").getAbsolutePath()); } public static interface CLibrary extends Library { @@ -98,7 +101,6 @@ public void testLoadCLibrary() { load(); } - private static final String UNICODE = "\u0444\u043b\u0441\u0432\u0443"; private void copy(File src, File dst) throws Exception { FileInputStream is = new FileInputStream(src); FileOutputStream os = new FileOutputStream(dst); @@ -116,32 +118,21 @@ private void copy(File src, File dst) throws Exception { } public void testLoadLibraryWithUnicodeName() throws Exception { - String tmp = System.getProperty("java.io.tmpdir"); - String libName = System.mapLibraryName("jnidispatch"); - File src = new File(BUILDDIR + "/native", libName); + File tmpdir = Native.getTempDir(); + String libName = NativeLibrary.mapSharedLibraryName("testlib"); + File src = new File(TESTPATH, libName); if (Platform.isWindowsCE()) { src = new File("/Storage Card", libName); } assertTrue("Expected JNA native library at " + src + " is missing", src.exists()); - String newLibName = UNICODE; - if (libName.startsWith("lib")) - newLibName = "lib" + newLibName; - int dot = libName.lastIndexOf("."); - if (dot != -1) { - if (Platform.isMac()) { - newLibName += ".dylib"; - } - else { - newLibName += libName.substring(dot, libName.length()); - } - } - File dst = new File(tmp, newLibName); + final String UNICODE = "\u0444\u043b\u0441\u0432\u0443"; + + String newLibName = libName.replace("testlib", UNICODE); + File dst = new File(tmpdir, newLibName); dst.deleteOnExit(); copy(src, dst); - NativeLibrary.addSearchPath(UNICODE, tmp); - NativeLibrary nl = NativeLibrary.getInstance(UNICODE); - nl.dispose(); + NativeLibrary.getInstance(UNICODE, new TestLoader(tmpdir)); } public void testHandleObjectMethods() { diff --git a/test/com/sun/jna/NativeLibraryTest.java b/test/com/sun/jna/NativeLibraryTest.java index 73690ad51a..bf81a8fd7b 100644 --- a/test/com/sun/jna/NativeLibraryTest.java +++ b/test/com/sun/jna/NativeLibraryTest.java @@ -26,6 +26,31 @@ public class NativeLibraryTest extends TestCase { public static interface TestLibrary extends Library { int callCount(); } + + public void testMapSharedLibraryName() { + final Object[][] MAPPINGS = { + { Platform.MAC, "lib", ".dylib" }, + { Platform.LINUX, "lib", ".so" }, + { Platform.WINDOWS, "", ".dll" }, + { Platform.SOLARIS, "lib", ".so" }, + { Platform.FREEBSD, "lib", ".so" }, + { Platform.OPENBSD, "lib", ".so" }, + { Platform.WINDOWSCE, "", ".dll" }, + { Platform.AIX, "lib", ".a" }, + { Platform.ANDROID, "lib", ".so" }, + { Platform.GNU, "lib", ".so" }, + { Platform.KFREEBSD, "lib", ".so" }, + }; + for (int i=0;i < MAPPINGS.length;i++) { + int osType = ((Integer)MAPPINGS[i][0]).intValue(); + if (osType == Platform.getOSType()) { + assertEquals("Wrong shared library name mapping", + MAPPINGS[i][1] + "testlib" + MAPPINGS[i][2], + NativeLibrary.mapSharedLibraryName("testlib")); + } + } + } + public void testGCNativeLibrary() throws Exception { NativeLibrary lib = NativeLibrary.getInstance("testlib"); WeakReference ref = new WeakReference(lib); diff --git a/test/com/sun/jna/NativeTest.java b/test/com/sun/jna/NativeTest.java index 8458cd30f7..20579df245 100644 --- a/test/com/sun/jna/NativeTest.java +++ b/test/com/sun/jna/NativeTest.java @@ -244,62 +244,6 @@ public void testToCharArray() { assertEquals("Wrong char array contents: " + new String(buf), VALUE, new String(buf, 0, buf.length-1)); } - public void testOSPrefix() { - assertEquals("Wrong resource path", "win32-x86", - Native.getNativeLibraryResourcePrefix(Platform.WINDOWS, - "x86", "Windows")); - assertEquals("Wrong resource path Windows/i386", "win32-x86", - Native.getNativeLibraryResourcePrefix(Platform.WINDOWS, - "i386", "Windows")); - assertEquals("Wrong resource path Windows CE/arm", "w32ce-arm", - Native.getNativeLibraryResourcePrefix(Platform.WINDOWSCE, - "arm", "Windows CE")); - assertEquals("Wrong resource path Mac/x86", "darwin", - Native.getNativeLibraryResourcePrefix(Platform.MAC, - "x86", "Darwin")); - assertEquals("Wrong resource path Mac/x86", "darwin", - Native.getNativeLibraryResourcePrefix(Platform.MAC, - "i386", "Darwin")); - assertEquals("Wrong resource path Mac/x86_64", "darwin", - Native.getNativeLibraryResourcePrefix(Platform.MAC, - "x86_64", "Mac")); - assertEquals("Wrong resource path Solaris/sparc", "sunos-sparc", - Native.getNativeLibraryResourcePrefix(Platform.SOLARIS, - "sparc", "Solaris")); - assertEquals("Wrong resource path SunOS/sparcv9", "sunos-sparcv9", - Native.getNativeLibraryResourcePrefix(Platform.SOLARIS, - "sparcv9", "SunOS")); - assertEquals("Wrong resource path Linux/i386", "linux-x86", - Native.getNativeLibraryResourcePrefix(Platform.LINUX, - "i386", "Linux/Gnu")); - assertEquals("Wrong resource path Linux/x86", "linux-x86", - Native.getNativeLibraryResourcePrefix(Platform.LINUX, - "x86", "Linux")); - assertEquals("Wrong resource path Linux/x86", "linux-x86-64", - Native.getNativeLibraryResourcePrefix(Platform.LINUX, - "x86_64", "Linux")); - assertEquals("Wrong resource path Linux/x86", "linux-x86-64", - Native.getNativeLibraryResourcePrefix(Platform.LINUX, - "amd64", "Linux")); - assertEquals("Wrong resource path Linux/ppc", "linux-ppc", - Native.getNativeLibraryResourcePrefix(Platform.LINUX, - "powerpc", "Linux")); - assertEquals("Wrong resource path OpenBSD/x86", "openbsd-x86", - Native.getNativeLibraryResourcePrefix(Platform.OPENBSD, - "x86", "OpenBSD")); - assertEquals("Wrong resource path FreeBSD/x86", "freebsd-x86", - Native.getNativeLibraryResourcePrefix(Platform.FREEBSD, - "x86", "FreeBSD")); - assertEquals("Wrong resource path Linux/armv7l (android)", "android-arm", - Native.getNativeLibraryResourcePrefix(Platform.ANDROID, - "armv7l", "Linux")); - - assertEquals("Wrong resource path other/other", "name-ppc", - Native.getNativeLibraryResourcePrefix(Platform.UNSPECIFIED, - "PowerPC", "Name Of System")); - - } - public static class DirectMapping { public static class DirectStructure extends Structure { public int field; diff --git a/test/com/sun/jna/Paths.java b/test/com/sun/jna/Paths.java new file mode 100644 index 0000000000..49c163e725 --- /dev/null +++ b/test/com/sun/jna/Paths.java @@ -0,0 +1,25 @@ +/* Copyright (c) 2009 Timothy Wall, All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package com.sun.jna; + +public interface Paths { + String BUILDDIR = Platform.isWindowsCE() + ? "/Storage Card" + : System.getProperty("jna.builddir", + "build" + (Platform.is64Bit() ? "-d64" : "")); + String CLASSES = BUILDDIR + (Platform.isWindowsCE() ? "" : "/classes"); + String JNAJAR = BUILDDIR + "/jna.jar"; + + String TESTPATH = Platform.isWindowsCE() ? "/Storage Card/" : BUILDDIR + "/native/"; + String TESTJAR = BUILDDIR + "/jna-test.jar"; +} diff --git a/test/com/sun/jna/PerformanceTest.java b/test/com/sun/jna/PerformanceTest.java index 61e03071d8..5d51c8e7c6 100644 --- a/test/com/sun/jna/PerformanceTest.java +++ b/test/com/sun/jna/PerformanceTest.java @@ -28,22 +28,15 @@ import com.sun.jna.DirectTest.TestLibrary; //@SuppressWarnings("unused") -public class PerformanceTest extends TestCase { +public class PerformanceTest extends TestCase implements Paths { public void testEmpty() { } - private static final String BUILDDIR = - System.getProperty("jna.builddir", - "build" + (Platform.is64Bit() ? "-d64" : "")); - private static class JNI { static { - String path = BUILDDIR + "/native/" + System.mapLibraryName("testlib");; + String path = TESTPATH + NativeLibrary.mapSharedLibraryName("testlib");; if (!new File(path).isAbsolute()) { - path = System.getProperty("user.dir") + "/" + path; - } - if (path.endsWith(".jnilib")) { - path = path.replace(".jnilib", ".dylib"); + path = new File(path).getAbsolutePath(); } System.load(path); } diff --git a/test/com/sun/jna/PlatformTest.java b/test/com/sun/jna/PlatformTest.java new file mode 100644 index 0000000000..6e679d51e0 --- /dev/null +++ b/test/com/sun/jna/PlatformTest.java @@ -0,0 +1,80 @@ +/* Copyright (c) 20013 Timothy Wall, All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package com.sun.jna; + +import junit.framework.TestCase; + +//@SuppressWarnings("unused") +public class PlatformTest extends TestCase { + + public void testOSPrefix() { + assertEquals("Wrong resource path", "win32-x86", + Platform.getNativeLibraryResourcePrefix(Platform.WINDOWS, + "x86", "Windows")); + assertEquals("Wrong resource path Windows/i386", "win32-x86", + Platform.getNativeLibraryResourcePrefix(Platform.WINDOWS, + "i386", "Windows")); + assertEquals("Wrong resource path Windows CE/arm", "w32ce-arm", + Platform.getNativeLibraryResourcePrefix(Platform.WINDOWSCE, + "arm", "Windows CE")); + assertEquals("Wrong resource path Mac/x86", "darwin", + Platform.getNativeLibraryResourcePrefix(Platform.MAC, + "x86", "Darwin")); + assertEquals("Wrong resource path Mac/x86", "darwin", + Platform.getNativeLibraryResourcePrefix(Platform.MAC, + "i386", "Darwin")); + assertEquals("Wrong resource path Mac/x86_64", "darwin", + Platform.getNativeLibraryResourcePrefix(Platform.MAC, + "x86_64", "Mac")); + assertEquals("Wrong resource path Solaris/sparc", "sunos-sparc", + Platform.getNativeLibraryResourcePrefix(Platform.SOLARIS, + "sparc", "Solaris")); + assertEquals("Wrong resource path SunOS/sparcv9", "sunos-sparcv9", + Platform.getNativeLibraryResourcePrefix(Platform.SOLARIS, + "sparcv9", "SunOS")); + assertEquals("Wrong resource path Linux/i386", "linux-x86", + Platform.getNativeLibraryResourcePrefix(Platform.LINUX, + "i386", "Linux/Gnu")); + assertEquals("Wrong resource path Linux/x86", "linux-x86", + Platform.getNativeLibraryResourcePrefix(Platform.LINUX, + "x86", "Linux")); + assertEquals("Wrong resource path Linux/x86", "linux-x86-64", + Platform.getNativeLibraryResourcePrefix(Platform.LINUX, + "x86_64", "Linux")); + assertEquals("Wrong resource path Linux/x86", "linux-x86-64", + Platform.getNativeLibraryResourcePrefix(Platform.LINUX, + "amd64", "Linux")); + assertEquals("Wrong resource path Linux/ppc", "linux-ppc", + Platform.getNativeLibraryResourcePrefix(Platform.LINUX, + "powerpc", "Linux")); + assertEquals("Wrong resource path OpenBSD/x86", "openbsd-x86", + Platform.getNativeLibraryResourcePrefix(Platform.OPENBSD, + "x86", "OpenBSD")); + assertEquals("Wrong resource path FreeBSD/x86", "freebsd-x86", + Platform.getNativeLibraryResourcePrefix(Platform.FREEBSD, + "x86", "FreeBSD")); + assertEquals("Wrong resource path Linux/armv7l (android)", "android-arm", + Platform.getNativeLibraryResourcePrefix(Platform.ANDROID, + "armv7l", "Linux")); + + assertEquals("Wrong resource path other/other", "name-ppc", + Platform.getNativeLibraryResourcePrefix(Platform.UNSPECIFIED, + "PowerPC", "Name Of System")); + + } + + + public static void main(String[] args) { + junit.textui.TestRunner.run(PlatformTest.class); + } +} diff --git a/test/com/sun/jna/WebStartTest.java b/test/com/sun/jna/WebStartTest.java index a5c7f9db1e..b451e48991 100644 --- a/test/com/sun/jna/WebStartTest.java +++ b/test/com/sun/jna/WebStartTest.java @@ -40,7 +40,7 @@ * Run tests under web start * Works under OSX, windows, and linux. */ -public class WebStartTest extends TestCase { +public class WebStartTest extends TestCase implements Paths { // Provide a policy file for unsigned jars // Unfortunately this does not allow native libraries @@ -123,11 +123,8 @@ public void testJNLPFindLibraryFailure() { } private void runTestUnderWebStart(String testClass, String testMethod) throws Exception { - String BUILDDIR = System.getProperty("jna.builddir", - "build" - + (Platform.is64Bit() - ? "-d64" : "")); - String codebase = new File(BUILDDIR, "jws").toURI().toURL().toString(); + String dir = System.getProperty("jna.builddir", BUILDDIR); + String codebase = new File(dir, "jws").toURI().toURL().toString(); ServerSocket s = new ServerSocket(0); s.setSoTimeout(120000); From 8d376755dc3c39aaeb8c5b8f4bb01482a9160248 Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Sun, 21 Apr 2013 09:33:33 -0400 Subject: [PATCH 02/15] clean up some function naming fix typo --- native/Makefile | 2 ++ native/callback.c | 10 +++++----- native/dispatch.c | 32 +++++++++++++++--------------- native/dispatch.h | 10 +++++----- test/com/sun/jna/PlatformTest.java | 2 +- 5 files changed, 29 insertions(+), 27 deletions(-) diff --git a/native/Makefile b/native/Makefile index c027283263..aecfa042cd 100644 --- a/native/Makefile +++ b/native/Makefile @@ -367,6 +367,8 @@ $(LIBRARY): $(JNIDISPATCH_OBJS) $(FFI_LIB) $(TESTLIB): $(BUILD)/testlib.o $(LD) $(LDFLAGS) $< $(LIBS) +# These targets provide for different shared library loading methods +# without getting into native library load conflicts $(TESTLIB_JAR) $(TESTLIB_PATH) $(TESTLIB_TRUNC): $(TESTLIB) @cp $< $@ diff --git a/native/callback.c b/native/callback.c index 87c605e2a8..9812dc41f4 100644 --- a/native/callback.c +++ b/native/callback.c @@ -538,7 +538,7 @@ static void make_thread_keys() { /** Store the requested detach state for the current thread. */ void -jnidispatch_detach(jboolean d) { +JNA_detach(jboolean d) { if (!TLS_SET(tls_detach_key, L2A((jlong)(d?THREAD_DETACH:THREAD_LEAVE_ATTACHED)))) { fprintf(stderr, "JNA: unable to set thread-local detach value\n"); } @@ -546,7 +546,7 @@ jnidispatch_detach(jboolean d) { /** Store the value of errno/GetLastError in TLS */ void -jnidispatch_set_last_error(int err) { +JNA_set_last_error(int err) { if (!TLS_SET(tls_errno_key, L2A((jlong)err))) { fprintf(stderr, "JNA: unable to set thread-local errno value\n"); } @@ -554,7 +554,7 @@ jnidispatch_set_last_error(int err) { /** Store the value of errno/GetLastError in TLS */ int -jnidispatch_get_last_error() { +JNA_get_last_error() { return (int)A2L(TLS_GET(tls_errno_key)); } @@ -645,7 +645,7 @@ callback_dispatch(ffi_cif* cif, void* resp, void** cbargs, void* user_data) { } const char* -jnidispatch_callback_init(JNIEnv* env) { +JNA_callback_init(JNIEnv* env) { #ifndef _WIN32 static pthread_once_t key_once = PTHREAD_ONCE_INIT; pthread_once(&key_once, make_thread_keys); @@ -657,7 +657,7 @@ jnidispatch_callback_init(JNIEnv* env) { } void -jnidispatch_callback_dispose(JNIEnv* env) { +JNA_callback_dispose(JNIEnv* env) { if (classObject) { (*env)->DeleteWeakGlobalRef(env, classObject); classObject = NULL; diff --git a/native/dispatch.c b/native/dispatch.c index 8afc6901d9..8b83c12052 100644 --- a/native/dispatch.c +++ b/native/dispatch.c @@ -2,7 +2,7 @@ * @(#)dispatch.c 1.9 98/03/22 * * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved. - * Copyright (c) 2007-2012 Timothy Wall. All Rights Reserved. + * Copyright (c) 2007-2013 Timothy Wall. All Rights Reserved. * Copyright (c) 2007 Wayne Meissner. All Rights Reserved. * * This library is free software; you can redistribute it and/or @@ -28,6 +28,12 @@ #include #define STRTYPE wchar_t* #define NAME2CSTR(ENV,JSTR) newWideCString(ENV,JSTR) +#ifdef _WIN32_WCE +#include +#define DEFAULT_LOAD_OPTS 0 /* altered search path unsupported on CE */ +#undef GetProcAddress +#define GetProcAddress GetProcAddressA +#else /* See http://msdn.microsoft.com/en-us/library/ms682586(VS.85).aspx: * "Note that the standard search strategy and the alternate search strategy * specified by LoadLibraryEx with LOAD_WITH_ALTERED_SEARCH_PATH differ in @@ -35,12 +41,6 @@ * directory, and the alternate search begins in the directory of the * executable module that LoadLibraryEx is loading." */ -#ifdef _WIN32_WCE -#include -#define DEFAULT_LOAD_OPTS 0 /* altered search path unsupported on CE */ -#undef GetProcAddress -#define GetProcAddress GetProcAddressA -#else #define DEFAULT_LOAD_OPTS LOAD_WITH_ALTERED_SEARCH_PATH #endif #define LOAD_LIBRARY(NAME,OPTS) (NAME ? LoadLibraryExW(NAME, NULL, OPTS) : GetModuleHandleW(NULL)) @@ -571,7 +571,7 @@ dispatch(JNIEnv *env, void* func, jint flags, jobjectArray arr, } } else if (preserve_last_error) { - jnidispatch_set_last_error(GET_LAST_ERROR()); + JNA_set_last_error(GET_LAST_ERROR()); } PROTECTED_END(do { throw_type=EError;throw_msg="Invalid memory access";} while(0)); } @@ -1226,7 +1226,7 @@ get_system_property(JNIEnv* env, const char* name, jboolean wide) { } static const char* -jnidispatch_init(JNIEnv* env) { +JNA_init(JNIEnv* env) { if (!LOAD_CREF(env, Object, "java/lang/Object")) return "java.lang.Object"; if (!LOAD_CREF(env, Class, "java/lang/Class")) return "java.lang.Class"; if (!LOAD_CREF(env, Method, "java/lang/reflect/Method")) return "java.lang.reflect.Method"; @@ -1718,7 +1718,7 @@ method_handler(ffi_cif* cif, void* volatile resp, void** argp, void *cdata) { } } else if (preserve_last_error) { - jnidispatch_set_last_error(GET_LAST_ERROR()); + JNA_set_last_error(GET_LAST_ERROR()); } PROTECTED_END(do { throw_type=EError;throw_msg="Invalid memory access"; } while(0)); } @@ -2939,13 +2939,13 @@ Java_com_sun_jna_Native_getPreserveLastError(JNIEnv *UNUSED(env), jclass UNUSED( JNIEXPORT void JNICALL Java_com_sun_jna_Native_setLastError(JNIEnv *env, jclass UNUSED(classp), jint code) { - jnidispatch_set_last_error(code); + JNA_set_last_error(code); SET_LAST_ERROR(code); } JNIEXPORT jint JNICALL Java_com_sun_jna_Native_getLastError(JNIEnv *env, jclass UNUSED(classp)) { - return jnidispatch_get_last_error(); + return JNA_get_last_error(); } JNIEXPORT jstring JNICALL @@ -2978,11 +2978,11 @@ JNI_OnLoad(JavaVM *jvm, void *UNUSED(reserved)) { } } - if ((err = jnidispatch_init(env)) != NULL) { + if ((err = JNA_init(env)) != NULL) { fprintf(stderr, "JNA: Problems loading core IDs: %s\n", err); result = 0; } - else if ((err = jnidispatch_callback_init(env)) != NULL) { + else if ((err = JNA_callback_init(env)) != NULL) { fprintf(stderr, "JNA: Problems loading callback IDs: %s\n", err); result = 0; } @@ -3034,7 +3034,7 @@ JNI_OnUnload(JavaVM *vm, void *UNUSED(reserved)) { } } - jnidispatch_callback_dispose(env); + JNA_callback_dispose(env); #ifdef JAWT_HEADLESS_HACK if (jawt_handle != NULL) { @@ -3249,7 +3249,7 @@ Java_com_sun_jna_Native_initialize_1ffi_1type(JNIEnv *env, jclass UNUSED(cls), j JNIEXPORT void JNICALL Java_com_sun_jna_Native_detach(JNIEnv* env, jclass UNUSED(cls), jboolean d) { - jnidispatch_detach(d); + JNA_detach(d); } #ifdef __cplusplus diff --git a/native/dispatch.h b/native/dispatch.h index 3394b4aa50..cd65853469 100644 --- a/native/dispatch.h +++ b/native/dispatch.h @@ -174,11 +174,11 @@ extern void throwByName(JNIEnv *env, const char *name, const char *msg); extern int get_jtype(JNIEnv*, jclass); extern ffi_type* get_ffi_type(JNIEnv*, jclass, char); extern ffi_type* get_ffi_rtype(JNIEnv*, jclass, char); -extern const char* jnidispatch_callback_init(JNIEnv*); -extern void jnidispatch_set_last_error(int); -extern int jnidispatch_get_last_error(); -extern void jnidispatch_callback_dispose(JNIEnv*); -extern void jnidispatch_detach(jboolean); +extern const char* JNA_callback_init(JNIEnv*); +extern void JNA_set_last_error(int); +extern int JNA_get_last_error(); +extern void JNA_callback_dispose(JNIEnv*); +extern void JNA_detach(jboolean); extern callback* create_callback(JNIEnv*, jobject, jobject, jobjectArray, jclass, callconv_t, jint); diff --git a/test/com/sun/jna/PlatformTest.java b/test/com/sun/jna/PlatformTest.java index 6e679d51e0..c4813ded6e 100644 --- a/test/com/sun/jna/PlatformTest.java +++ b/test/com/sun/jna/PlatformTest.java @@ -1,4 +1,4 @@ -/* Copyright (c) 20013 Timothy Wall, All Rights Reserved +/* Copyright (c) 2013 Timothy Wall, All Rights Reserved * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public From ea56252d2f4e06576f8e25dab90d7c476d26d3ca Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Sun, 21 Apr 2013 09:46:56 -0400 Subject: [PATCH 03/15] fix javadoc warnings --- src/com/sun/jna/Native.java | 5 +++-- src/com/sun/jna/NativeLibrary.java | 6 +++--- src/com/sun/jna/overview.html | 5 +++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/com/sun/jna/Native.java b/src/com/sun/jna/Native.java index 4f2a021936..200c48d1d3 100644 --- a/src/com/sun/jna/Native.java +++ b/src/com/sun/jna/Native.java @@ -716,7 +716,7 @@ static boolean isUnpacked(File file) { * no transformations of the library name are performed. If only the base * name is given, the resource path is attempted both with and without * {@link Platform#RESOURCE_PREFIX}, after mapping the library name via - * {@link NativeLibrary#mapSharedLibraryName()}. + * {@link NativeLibrary#mapSharedLibraryName(String)}. * @return File indicating extracted resource on disk * @throws IOException if resource not found */ @@ -731,7 +731,7 @@ public static File extractFromResourcePath(String name) throws IOException { * no transformations of the library name are performed. If only the base * name is given, the resource path is attempted both with and without * {@link Platform#RESOURCE_PREFIX}, after mapping the library name via - * {@link NativeLibrary#mapSharedLibraryName()}. + * {@link NativeLibrary#mapSharedLibraryName(String)}. * @param loader Class loader to use to load resources * @return File indicating extracted resource on disk * @throws IOException if resource not found @@ -747,6 +747,7 @@ public static File extractFromResourcePath(String name, ClassLoader loader) thro } URL url = loader.getResource(resourcePath); if (url == null && resourcePath.startsWith(Platform.RESOURCE_PREFIX)) { + // If not found with the standard resource prefix, try without it url = loader.getResource(libname); } if (url == null) { diff --git a/src/com/sun/jna/NativeLibrary.java b/src/com/sun/jna/NativeLibrary.java index c03f7a4d16..789d61ebd0 100644 --- a/src/com/sun/jna/NativeLibrary.java +++ b/src/com/sun/jna/NativeLibrary.java @@ -299,9 +299,9 @@ public static final NativeLibrary getInstance(String libraryName) { * included somewhere in the classpath, either bundled in a jar file * or as a plain file within the classpath. */ - public static final NativeLibrary getInstance(String libraryName, ClassLoader loader) { + public static final NativeLibrary getInstance(String libraryName, ClassLoader classLoader) { Map map = new HashMap(); - map.put(Library.OPTION_CLASSLOADER, loader); + map.put(Library.OPTION_CLASSLOADER, classLoader); return getInstance(libraryName, map); } @@ -328,7 +328,7 @@ public static final NativeLibrary getInstance(String libraryName, Map options) { // Use current process to load libraries we know are already // loaded by the VM to ensure we get the correct version - if ((Platform.isLinux() || Platform.isAix()) + if ((Platform.isLinux() || Platform.isAIX()) && Platform.C_LIBRARY_NAME.equals(libraryName)) { libraryName = null; } diff --git a/src/com/sun/jna/overview.html b/src/com/sun/jna/overview.html index 40a751b381..e66bd1b14c 100644 --- a/src/com/sun/jna/overview.html +++ b/src/com/sun/jna/overview.html @@ -128,8 +128,9 @@

Library Mapping

conflicts if there are several incompatible versions of a library available.

The search path for loaded native libraries may be modified by -setting jna.library.path and a few other properties. See {@link -com.sun.jna.NativeLibrary} for details. +setting jna.library.path and a few other properties. You may +also bundle native libraries in a jar file and have JNA automatically extract +them for loading. See {@link com.sun.jna.NativeLibrary} for details.

Table of Contents From dbeda7f67dea9a290b683e230118d8d40b5ccd20 Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Sun, 21 Apr 2013 22:37:47 -0400 Subject: [PATCH 04/15] fix some tests under clover --- test/com/sun/jna/DirectTest.java | 3 ++- test/com/sun/jna/JNAUnloadTest.java | 2 +- test/com/sun/jna/LibraryLoadTest.java | 3 ++- test/com/sun/jna/Paths.java | 18 +++++++++++++++++- test/com/sun/jna/PerformanceTest.java | 3 --- test/com/sun/jna/WebStartTest.java | 4 +--- 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/test/com/sun/jna/DirectTest.java b/test/com/sun/jna/DirectTest.java index 1891e95cbe..7fa5a28b01 100644 --- a/test/com/sun/jna/DirectTest.java +++ b/test/com/sun/jna/DirectTest.java @@ -110,7 +110,7 @@ public TestLoader() throws MalformedURLException { : new URL[] { new File(BUILDDIR + "/classes").toURI().toURL(), new File(BUILDDIR + "/test-classes").toURI().toURL(), - }, null); + }, new CloverLoader()); } protected Class findClass(String name) throws ClassNotFoundException { String boot = System.getProperty("jna.boot.library.path"); @@ -125,6 +125,7 @@ protected Class findClass(String name) throws ClassNotFoundException { } } + // Fails under clover public void testRegisterMethods() throws Exception { // Use a dedicated class loader to ensure the class can be gc'd String name = "com.sun.jna.DirectTest$MathLibrary"; diff --git a/test/com/sun/jna/JNAUnloadTest.java b/test/com/sun/jna/JNAUnloadTest.java index 75aa85b1df..4faaa07035 100644 --- a/test/com/sun/jna/JNAUnloadTest.java +++ b/test/com/sun/jna/JNAUnloadTest.java @@ -32,7 +32,7 @@ public TestLoader(boolean fromJar) throws MalformedURLException { Platform.isWindowsCE() ? new File("/Storage Card/" + (fromJar ? "jna.jar" : "test.jar")).toURI().toURL() : new File(BUILDDIR + (fromJar ? "/jna.jar" : "/classes")).toURI().toURL(), - }, null); + }, new CloverLoader()); if (fromJar) { assertJarExists(); } diff --git a/test/com/sun/jna/LibraryLoadTest.java b/test/com/sun/jna/LibraryLoadTest.java index 2347bf8a1c..da61afe8a4 100644 --- a/test/com/sun/jna/LibraryLoadTest.java +++ b/test/com/sun/jna/LibraryLoadTest.java @@ -29,7 +29,8 @@ public class LibraryLoadTest extends TestCase implements Paths { private class TestLoader extends URLClassLoader { public TestLoader(File path) throws MalformedURLException { - super(new URL[] { path.toURI().toURL() }, null); + super(new URL[] { path.toURI().toURL(), }, + new CloverLoader()); } } diff --git a/test/com/sun/jna/Paths.java b/test/com/sun/jna/Paths.java index 49c163e725..c7f8592199 100644 --- a/test/com/sun/jna/Paths.java +++ b/test/com/sun/jna/Paths.java @@ -12,11 +12,27 @@ */ package com.sun.jna; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + public interface Paths { + boolean USING_CLOVER = System.getProperty("java.class.path").indexOf("clover") != -1; + /** Use this as a parent class loader to ensure clover can be loaded. */ + class CloverLoader extends URLClassLoader { + public CloverLoader() throws MalformedURLException{ + super(new URL[] { + new File(USING_CLOVER ? "lib/clover.jar" : "/dev/null").toURI().toURL() + }, null); + } + } String BUILDDIR = Platform.isWindowsCE() ? "/Storage Card" : System.getProperty("jna.builddir", - "build" + (Platform.is64Bit() ? "-d64" : "")); + USING_CLOVER + ? "build.clover" + : "build" + (Platform.is64Bit() ? "-d64" : "")); String CLASSES = BUILDDIR + (Platform.isWindowsCE() ? "" : "/classes"); String JNAJAR = BUILDDIR + "/jna.jar"; diff --git a/test/com/sun/jna/PerformanceTest.java b/test/com/sun/jna/PerformanceTest.java index 5d51c8e7c6..4162b3271f 100644 --- a/test/com/sun/jna/PerformanceTest.java +++ b/test/com/sun/jna/PerformanceTest.java @@ -17,9 +17,6 @@ import com.sun.jna.ptr.PointerByReference; import java.lang.ref.*; import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; diff --git a/test/com/sun/jna/WebStartTest.java b/test/com/sun/jna/WebStartTest.java index b451e48991..1fc50258ed 100644 --- a/test/com/sun/jna/WebStartTest.java +++ b/test/com/sun/jna/WebStartTest.java @@ -136,10 +136,8 @@ private void runTestUnderWebStart(String testClass, String testMethod) throws Ex contents = contents.replace("{CODEBASE}", codebase); contents = contents.replace("{JNLP_FILE}", jnlp.toURI().toURL().toString()); contents = contents.replace("{PORT}", String.valueOf(port)); - boolean clover = - System.getProperty("java.class.path").indexOf("clover") != -1; contents = contents.replace("{CLOVER}", - clover ? "" : ""); + USING_CLOVER ? "" : ""); try { OutputStream os = new FileOutputStream(jnlp); From b4f0f21421c7e9fd72c527c64d7eee4008d505a0 Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Mon, 22 Apr 2013 08:48:27 -0400 Subject: [PATCH 05/15] fixes for oracle openjdk --- src/com/sun/jna/Native.java | 8 +++++--- src/com/sun/jna/Structure.java | 4 ++-- test/com/sun/jna/JNAUnloadTest.java | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/com/sun/jna/Native.java b/src/com/sun/jna/Native.java index 200c48d1d3..8fb6f417f3 100644 --- a/src/com/sun/jna/Native.java +++ b/src/com/sun/jna/Native.java @@ -626,7 +626,7 @@ private static void loadNativeDispatchLibrary() { StringTokenizer dirs = new StringTokenizer(bootPath, File.pathSeparator); while (dirs.hasMoreTokens()) { String dir = dirs.nextToken(); - File file = new File(new File(dir), System.mapLibraryName(libName)); + File file = new File(new File(dir), System.mapLibraryName(libName).replace(".dylib", ".jnilib")); String path = file.getAbsolutePath(); if (file.exists()) { try { @@ -684,10 +684,12 @@ private static void loadNativeDispatchLibrary() { */ private static void loadNativeDispatchLibraryFromClasspath() { try { - String libName = "/com/sun/jna/" + Platform.RESOURCE_PREFIX + "/" + System.mapLibraryName("jnidispatch"); + String libName = "/com/sun/jna/" + Platform.RESOURCE_PREFIX + "/" + System.mapLibraryName("jnidispatch").replace(".dylib", ".jnilib"); File lib = extractFromResourcePath(libName, Native.class.getClassLoader()); if (lib == null) { - throw new UnsatisfiedLinkError("Could not find JNA native support"); + if (lib == null) { + throw new UnsatisfiedLinkError("Could not find JNA native support"); + } } System.load(lib.getAbsolutePath()); nativeLibraryPath = lib.getAbsolutePath(); diff --git a/src/com/sun/jna/Structure.java b/src/com/sun/jna/Structure.java index 048d41f9c2..68742f13e1 100644 --- a/src/com/sun/jna/Structure.java +++ b/src/com/sun/jna/Structure.java @@ -1283,9 +1283,9 @@ else if (actualAlignType == ALIGN_GNUC) { if (!isFirstElement || !(Platform.isMac() && isPPC)) { alignment = Math.min(MAX_GNUC_ALIGNMENT, alignment); } - if (!isFirstElement && Platform.isAix() && (type.getName().equals("double"))) { + if (!isFirstElement && Platform.isAIX() && (type.getName().equals("double"))) { alignment = 4; - } + } } return alignment; } diff --git a/test/com/sun/jna/JNAUnloadTest.java b/test/com/sun/jna/JNAUnloadTest.java index 4faaa07035..1e26357427 100644 --- a/test/com/sun/jna/JNAUnloadTest.java +++ b/test/com/sun/jna/JNAUnloadTest.java @@ -62,7 +62,7 @@ protected void assertJarExists() { protected void assertLibraryExists() { String osPrefix = Platform.getNativeLibraryResourcePrefix(); - String name = System.mapLibraryName("jnidispatch"); + String name = System.mapLibraryName("jnidispatch").replace(".dylib", ".jnilib"); File lib = new File(CLASSES + "/com/sun/jna/" + osPrefix + "/" + name); if (!lib.exists()) { throw new Error("Expected JNA library at " + lib + " is missing"); From 075efa649d61ea0fe3b07deda28bcaab233fcfb8 Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Wed, 24 Apr 2013 06:57:16 -0400 Subject: [PATCH 06/15] use a different error code on windows, fixes broken test --- test/com/sun/jna/LastErrorTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/com/sun/jna/LastErrorTest.java b/test/com/sun/jna/LastErrorTest.java index c0283fff8e..ff7692d975 100644 --- a/test/com/sun/jna/LastErrorTest.java +++ b/test/com/sun/jna/LastErrorTest.java @@ -75,10 +75,10 @@ public void testLastErrorPerThreadStorage() throws Exception { } } + private final int ERROR = Platform.isWindows() ? 1 : -1; public void testThrowLastError() { TestLibrary lib = (TestLibrary)Native.loadLibrary("testlib", TestLibrary.class, OPTIONS); - final int ERROR = -1; lib.noThrowLastError(ERROR); assertEquals("Last error not preserved", ERROR, Native.getLastError()); try { @@ -87,14 +87,13 @@ public void testThrowLastError() { } catch(LastErrorException e) { assertEquals("Exception should contain error code", ERROR, e.getErrorCode()); - assertTrue("Exception should include error message: " + e.getMessage(), e.getMessage().length() > 10); + assertTrue("Exception should include error message: '" + e.getMessage() + "'", e.getMessage().length() > 0); } } public void testThrowLastErrorDirect() { TestLibrary lib = new DirectTestLibrary(); - final int ERROR = -1; lib.noThrowLastError(ERROR); assertEquals("Last error not preserved", ERROR, Native.getLastError()); try { From 299b0a95175182fbd904db93865a9bbde711b616 Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Wed, 24 Apr 2013 08:11:49 -0400 Subject: [PATCH 07/15] catch attempts to run 64-bit tests on 32-bit javaws --- test/com/sun/jna/WebStartTest.java | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/test/com/sun/jna/WebStartTest.java b/test/com/sun/jna/WebStartTest.java index 1fc50258ed..0777f6e1ca 100644 --- a/test/com/sun/jna/WebStartTest.java +++ b/test/com/sun/jna/WebStartTest.java @@ -79,6 +79,9 @@ public class WebStartTest extends TestCase implements Paths { + " {PORT}\n" // NetX doesn't set javawebstart.version, so explicitly flag it + " javawebstart\n" + // Explicitly indicate the architecture we want to skip the test + // if we somehow got the wrong architecture javaws + + " arch64=" + Platform.is64Bit() + "\n" + ""; @@ -376,27 +379,31 @@ else if (!GraphicsEnvironment.isHeadless()) { } } + private static void sendResults(Throwable t, int port) throws IOException { + Socket s = new Socket(InetAddress.getLocalHost(), port); + OutputStream os = s.getOutputStream(); + t.printStackTrace(new PrintStream(os)); + s.close(); + } + private static void runTestCaseTest(String testClass, String method, int port) { try { TestCase test = (TestCase)Class.forName(testClass).newInstance(); test.setName(method); TestResult result = new TestResult(); test.run(result); - Socket s = new Socket(InetAddress.getLocalHost(), port); - OutputStream os = s.getOutputStream(); if (result.failureCount() != 0) { Enumeration e = result.failures(); Throwable t = ((TestFailure)e.nextElement()).thrownException(); - t.printStackTrace(new PrintStream(os)); + sendResults(t, port); } else if (result.errorCount() != 0) { Enumeration e = result.errors(); Throwable t = ((TestFailure)e.nextElement()).thrownException(); - t.printStackTrace(new PrintStream(os)); + sendResults(t, port); } // NOTE: System.exit with non-zero status causes an error dialog // on w32 sun "1.6.0_14" (build 1.6.0_14-b08) - s.close(); System.exit(0); } catch(Throwable e) { @@ -424,7 +431,7 @@ private static void showMessage(String msg, int timeout) { public static void main(String[] args) { try { - if (args.length == 4 + if (args.length >= 4 && "javawebstart".equals(args[3]) && !runningWebStart()) { System.setProperty("javawebstart.version", "fake"); @@ -437,6 +444,14 @@ public static void main(String[] args) { ? args[1] : "testLaunchedUnderWebStart"; int port = args.length > 2 ? Integer.parseInt(args[2]) : 8080; + + if (args.length >=5 + && "arch64=true".equals(args[4])) { + if (!Platform.is64Bit()) { + sendResults(new Error("Cannot run 64-bit test on 32-bit javaws"), port); + System.exit(0); + } + } runTestCaseTest(testClass, testMethod, port); } else { From 19d7775e9174557d9ebdd0e11284934f0d33a030 Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Wed, 24 Apr 2013 08:33:53 -0400 Subject: [PATCH 08/15] windows won't load libraries w/truncated name --- native/Makefile | 2 ++ src/com/sun/jna/NativeLibrary.java | 7 +++++-- test/com/sun/jna/LibraryLoadTest.java | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/native/Makefile b/native/Makefile index aecfa042cd..da6f3039fc 100644 --- a/native/Makefile +++ b/native/Makefile @@ -147,6 +147,7 @@ HOST_CONFIG=--host=arm-mingw32ce FFI_ENV+=LD=arm-mingw32ce-ld CPP=cpp CFLAGS="$(CDEFINES)" PHONEME=../phoneme/cdc/src JAVA_INCLUDES=-I$(PHONEME)/share/javavm/export -I$(PHONEME)/share -I$(PHONEME)/win32 -I$(PHONEME)/win32-arm +TESTLIB_TRUNC=$(BUILD)/testlib-truncated.dll endif ifeq ($(OS),win32) @@ -158,6 +159,7 @@ EXTRAOBJS=$(RSRC) STRIP=@echo LIBPFX= LIBSFX=.dll +TESTLIB_TRUNC=$(BUILD)/testlib-truncated.dll ifneq ($(ARCH),amd64) ifeq ($(CC),gcc) diff --git a/src/com/sun/jna/NativeLibrary.java b/src/com/sun/jna/NativeLibrary.java index 789d61ebd0..22b277deb9 100644 --- a/src/com/sun/jna/NativeLibrary.java +++ b/src/com/sun/jna/NativeLibrary.java @@ -119,6 +119,7 @@ private static int openFlags(Map options) { } private static NativeLibrary loadLibrary(String libraryName, Map options) { + boolean isAbsolutePath = new File(libraryName).isAbsolute(); List searchPath = new LinkedList(); int openFlags = openFlags(options); @@ -187,7 +188,9 @@ else if (Platform.isLinux()) { } } // Search framework libraries on OS X - else if (Platform.isMac() && !libraryName.endsWith(".dylib")) { + else if (Platform.isMac() + && !libraryName.endsWith(".dylib") + && !isAbsolutePath) { libraryPath = matchFramework(libraryName); if (libraryPath != null) { try { @@ -197,7 +200,7 @@ else if (Platform.isMac() && !libraryName.endsWith(".dylib")) { } } // Try the same library with a "lib" prefix - else if (Platform.isWindows()) { + else if (Platform.isWindows() && !isAbsolutePath) { libraryPath = findLibraryPath("lib" + libraryName, searchPath); try { handle = Native.open(libraryPath, openFlags); } catch(UnsatisfiedLinkError e2) { e = e2; } diff --git a/test/com/sun/jna/LibraryLoadTest.java b/test/com/sun/jna/LibraryLoadTest.java index da61afe8a4..98e40718f0 100644 --- a/test/com/sun/jna/LibraryLoadTest.java +++ b/test/com/sun/jna/LibraryLoadTest.java @@ -82,7 +82,9 @@ public void testLoadFromJarAbsolute() throws MalformedURLException { } public void testLoadExplicitAbsolutePath() throws MalformedURLException { - NativeLibrary.getInstance(new File(TESTPATH, "testlib-truncated").getAbsolutePath()); + // windows requires ".dll" suffix + String name = "testlib-truncated" + (Platform.isWindows() ? ".dll" : ""); + NativeLibrary.getInstance(new File(TESTPATH, name).getAbsolutePath()); } public static interface CLibrary extends Library { From 6b552cc03d3842f4c43bf24e199973beadafa77d Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Thu, 25 Apr 2013 08:22:16 -0400 Subject: [PATCH 09/15] fix framework loading on OSX --- src/com/sun/jna/NativeLibrary.java | 3 +- test/com/sun/jna/LibraryLoadTest.java | 43 +++++++++++++++++++++++++ test/com/sun/jna/NativeLibraryTest.java | 36 --------------------- 3 files changed, 44 insertions(+), 38 deletions(-) diff --git a/src/com/sun/jna/NativeLibrary.java b/src/com/sun/jna/NativeLibrary.java index 22b277deb9..83003ac004 100644 --- a/src/com/sun/jna/NativeLibrary.java +++ b/src/com/sun/jna/NativeLibrary.java @@ -189,8 +189,7 @@ else if (Platform.isLinux()) { } // Search framework libraries on OS X else if (Platform.isMac() - && !libraryName.endsWith(".dylib") - && !isAbsolutePath) { + && !libraryName.endsWith(".dylib")) { libraryPath = matchFramework(libraryName); if (libraryPath != null) { try { diff --git a/test/com/sun/jna/LibraryLoadTest.java b/test/com/sun/jna/LibraryLoadTest.java index 98e40718f0..9d44a46620 100644 --- a/test/com/sun/jna/LibraryLoadTest.java +++ b/test/com/sun/jna/LibraryLoadTest.java @@ -138,6 +138,49 @@ public void testLoadLibraryWithUnicodeName() throws Exception { NativeLibrary.getInstance(UNICODE, new TestLoader(tmpdir)); } + public void testLoadFrameworkLibrary() { + if (Platform.isMac()) { + final String PATH = "/System/Library/Frameworks/CoreServices.framework"; + assertTrue("CoreServices not present on this setup, expected at " + PATH, new File(PATH).exists()); + try { + NativeLibrary lib = NativeLibrary.getInstance("CoreServices"); + assertNotNull("CoreServices not found", lib); + } + catch(UnsatisfiedLinkError e) { + fail("Should search /System/Library/Frameworks"); + } + } + } + + public void testLoadFrameworkLibraryAbsolute() { + if (Platform.isMac()) { + final String PATH = "/System/Library/Frameworks/CoreServices"; + final String FRAMEWORK = PATH + ".framework"; + assertTrue("CoreServices not present on this setup, expected at " + FRAMEWORK, new File(FRAMEWORK).exists()); + try { + NativeLibrary lib = NativeLibrary.getInstance(PATH); + assertNotNull("CoreServices not found", lib); + } + catch(UnsatisfiedLinkError e) { + fail("Should try FRAMEWORK.framework/FRAMEWORK if the absolute framework (truncated) path given exists: " + e); + } + } + } + + public void testLoadFrameworkLibraryAbsoluteFull() { + if (Platform.isMac()) { + final String PATH = "/System/Library/Frameworks/CoreServices.framework/CoreServices"; + assertTrue("CoreServices not present on this setup, expected at " + PATH, new File(PATH).exists()); + try { + NativeLibrary lib = NativeLibrary.getInstance(PATH); + assertNotNull("CoreServices not found", lib); + } + catch(UnsatisfiedLinkError e) { + fail("Should try FRAMEWORK verbatim if the absolute path given exists: " + e); + } + } + } + public void testHandleObjectMethods() { CLibrary lib = (CLibrary)load(); String method = "toString"; diff --git a/test/com/sun/jna/NativeLibraryTest.java b/test/com/sun/jna/NativeLibraryTest.java index bf81a8fd7b..72d5d1dff9 100644 --- a/test/com/sun/jna/NativeLibraryTest.java +++ b/test/com/sun/jna/NativeLibraryTest.java @@ -141,42 +141,6 @@ public void testFunctionHoldsLibraryReference() throws Exception { assertNull("Library not GC'd", ref.get()); } - public void testLoadFrameworkLibrary() { - if (Platform.isMac()) { - try { - NativeLibrary lib = NativeLibrary.getInstance("CoreServices"); - assertNotNull("CoreServices not found", lib); - } - catch(UnsatisfiedLinkError e) { - fail("Should search /System/Library/Frameworks"); - } - } - } - - public void testLoadFrameworkLibraryAbsolute() { - if (Platform.isMac()) { - try { - NativeLibrary lib = NativeLibrary.getInstance("/System/Library/Frameworks/CoreServices"); - assertNotNull("CoreServices not found", lib); - } - catch(UnsatisfiedLinkError e) { - fail("Should try FRAMEWORK.framework/FRAMEWORK if absolute and exists"); - } - } - } - - public void testLoadFrameworkLibraryAbsoluteFull() { - if (Platform.isMac()) { - try { - NativeLibrary lib = NativeLibrary.getInstance("/System/Library/Frameworks/CoreServices.framework/CoreServices"); - assertNotNull("CoreServices not found", lib); - } - catch(UnsatisfiedLinkError e) { - fail("Should try FRAMEWORK if absolute and exists"); - } - } - } - public void testLookupGlobalVariable() { NativeLibrary lib = NativeLibrary.getInstance("testlib"); Pointer global = lib.getGlobalVariableAddress("test_global"); From 5db3b427709b4b60121d7cca1254d4bb747523bb Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Thu, 25 Apr 2013 08:22:37 -0400 Subject: [PATCH 10/15] consolidate compiler pragmas --- native/dispatch.c | 3 --- native/dispatch.h | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/native/dispatch.c b/native/dispatch.c index 8b83c12052..8205786360 100644 --- a/native/dispatch.c +++ b/native/dispatch.c @@ -17,9 +17,6 @@ */ #if defined(_WIN32) -#ifdef _MSC_VER -#pragma warning( disable : 4201 ) /* nameless struct/union (jni_md.h) */ -#endif #ifndef UNICODE #define UNICODE #endif diff --git a/native/dispatch.h b/native/dispatch.h index cd65853469..aea2d8d550 100644 --- a/native/dispatch.h +++ b/native/dispatch.h @@ -27,6 +27,7 @@ #pragma warning( disable : 4055 ) /* cast data pointer to function pointer */ #pragma warning( disable : 4204 ) /* structure initializer */ #pragma warning( disable : 4710 ) /* swprintf not inlined */ +#pragma warning( disable : 4201 ) /* nameless struct/union (jni_md.h) */ #else #include #endif /* _MSC_VER */ From 1af100013934aa2d06f48e4e7b1f2e37a579f2eb Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Tue, 30 Apr 2013 07:41:56 -0300 Subject: [PATCH 11/15] Add prominent links for mailing list and stackoverflow --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fff3472227..313932721d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Java Native Access (JNA) ======================== -The definitive JNA reference (including an overview and usage details) is in the [JavaDoc](http://twall.github.com/jna/3.5.2/javadoc/). Please read the [overview](http://twall.github.com/jna/3.5.2/javadoc/overview-summary.html#overview_description). +The definitive JNA reference (including an overview and usage details) is in the [JavaDoc](http://twall.github.com/jna/3.5.2/javadoc/). Please read the [overview](http://twall.github.com/jna/3.5.2/javadoc/overview-summary.html#overview_description). Questions, comments, or exploratory conversations should begin on the [mailing list](http://groups.google.com/group/jna-users), although you may find it easier to find answers to already-solved problems on [StackOverflow](http://stackoverflow.com/questions/tagged/jna). JNA provides Java programs easy access to native shared libraries (DLLs on Windows) without writing anything but Java codeā€”no JNI or native code is required. This functionality is comparable to Windows' Platform/Invoke and Python's ctypes. Access is dynamic at runtime without code generation. From 38b796a516440339936b96c907666b8e4a506a9c Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Wed, 1 May 2013 00:11:19 -0400 Subject: [PATCH 12/15] fix #223, union sizing/layout issue --- CHANGES.md | 1 + src/com/sun/jna/Structure.java | 48 +++++++--- src/com/sun/jna/Union.java | 75 ++++------------ test/com/sun/jna/StructureTest.java | 131 ++++++++++++++++------------ test/com/sun/jna/UnionTest.java | 51 ++++++----- 5 files changed, 160 insertions(+), 146 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 93ca4ce036..0a57637a1c 100755 --- a/CHANGES.md +++ b/CHANGES.md @@ -18,6 +18,7 @@ Bug Fixes * [#215](https://github.com/twall/jna/issues/215): Force use of XSI `strerror_r` on linux - [LionelCons](https://github.com/LionelCons). * [#214](https://github.com/twall/jna/issues/214): Don't map library names when an absolute path is provided - [@twall](https://github.com/twall). * [#218](https://github.com/twall/jna/issues/218): Explicitly handle broken Android SecurityManager implementation - [@twall](https://github.com/twall). +* [#223](https://github.com/twall/jna/issues/223): Fix layout/size derivation for unions - [@twall](https://github.com/twall). Release 3.5.2 ============= diff --git a/src/com/sun/jna/Structure.java b/src/com/sun/jna/Structure.java index 68742f13e1..8431799c17 100644 --- a/src/com/sun/jna/Structure.java +++ b/src/com/sun/jna/Structure.java @@ -114,7 +114,7 @@ public interface ByReference { } String arch = System.getProperty("os.arch").toLowerCase(); isPPC = "ppc".equals(arch) || "powerpc".equals(arch); isSPARC = "sparc".equals(arch); - isARM = arch.startsWith("arm"); + isARM = arch.startsWith("arm"); } /** Use the platform default alignment. */ @@ -216,7 +216,7 @@ TypeMapper getTypeMapper() { return typeMapper; } - /** Initialize the type mapper for this structure. + /** Initialize the type mapper for this structure. * If null, the default mapper for the * defining class will be used. */ @@ -871,7 +871,7 @@ private List sort(Collection c) { return list; } - /** Returns all field names (sorted) provided so far by + /** Returns all field names (sorted) provided so far by {@link #getFieldOrder} @param force set if results are required immediately @return null if not yet able to provide fields, and force is false. @@ -899,7 +899,7 @@ protected List getFields(boolean force) { Set orderedNames = new HashSet(fieldOrder); if (!orderedNames.equals(names)) { - throw new Error("Structure.getFieldOrder() on " + getClass() + throw new Error("Structure.getFieldOrder() on " + getClass() + " returns names (" + sort(fieldOrder) + ") which do not match declared field names (" @@ -991,6 +991,8 @@ private static class LayoutInfo { private int alignType = ALIGN_DEFAULT; private TypeMapper typeMapper; private boolean variable; + // For unions only, field on which the union FFI type info is based + private StructField typeInfoField; } private void validateField(String name, Class type) { @@ -1029,7 +1031,6 @@ private void validateFields() { members. */ private LayoutInfo deriveLayout(boolean force, boolean avoidFFIType) { - int calculatedSize = 0; List fields = getFields(force); if (fields == null) { @@ -1139,11 +1140,24 @@ else if (writeConverter != null || readConverter != null) { if ((calculatedSize % fieldAlignment) != 0) { calculatedSize += fieldAlignment - (calculatedSize % fieldAlignment); } - structField.offset = calculatedSize; - calculatedSize += structField.size; + if (this instanceof Union) { + structField.offset = 0; + calculatedSize = Math.max(calculatedSize, structField.size); + } + else { + structField.offset = calculatedSize; + calculatedSize += structField.size; + } // Save the field in our list info.fields.put(structField.name, structField); + + if (info.typeInfoField == null + || info.typeInfoField.size < structField.size + || (info.typeInfoField.size == structField.size + && Structure.class.isAssignableFrom(structField.type))) { + info.typeInfoField = structField; + } } if (calculatedSize > 0) { @@ -1504,7 +1518,7 @@ Pointer getTypeInfo() { This is typically most effective when a native call populates a large structure and you only need a few fields out of it. After the native call you can call {@link #readField(String)} on only the fields of - interest. + interest. */ public void setAutoSynch(boolean auto) { setAutoRead(auto); @@ -1544,7 +1558,7 @@ static Pointer getTypeInfo(Object obj) { return FFIType.get(obj); } - /** Called from native code only; same as {@link + /** Called from native code only; same as {@link * #newInstance(Class,Pointer)}, except that it additionally performs * {@link #conditionalAutoRead()}. */ @@ -1619,6 +1633,20 @@ public static Structure newInstance(Class type) throws IllegalArgumentException } } + /** Keep track of the largest aggregate field of the union to use for + * FFI type information. + */ + StructField typeInfoField() { + LayoutInfo info; + synchronized(layoutInfo) { + info = (LayoutInfo)layoutInfo.get(getClass()); + } + if (info != null) { + return info.typeInfoField; + } + return null; + } + static class StructField extends Object { public String name; public Class type; @@ -1704,7 +1732,7 @@ private FFIType(Structure ref) { ref.ensureAllocated(true); if (ref instanceof Union) { - StructField sf = ((Union)ref).biggestField; + StructField sf = ((Union)ref).typeInfoField(); els = new Pointer[] { get(ref.getFieldValue(sf.field), sf.type), null, diff --git a/src/com/sun/jna/Union.java b/src/com/sun/jna/Union.java index d899e81ba2..d08b50d482 100644 --- a/src/com/sun/jna/Union.java +++ b/src/com/sun/jna/Union.java @@ -1,14 +1,14 @@ /* Copyright (c) 2007-2012 Timothy Wall, All Rights Reserved - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * Lesser General Public License for more details. */ package com.sun.jna; @@ -20,18 +20,17 @@ /** Represents a native union. When writing to native memory, the field * corresponding to the type passed to {@link #setType} will be written * to native memory. Upon reading from native memory, Structure, String, - * or WString fields will not be initialized unless they are + * or WString fields will not be initialized unless they are * the current field as identified by a call to {@link #setType}. The current * field is always unset by default to avoid accidentally attempting to read - * a field that is not valid. In the case of a String, for instance, an + * a field that is not valid. In the case of a String, for instance, an * invalid pointer may result in a memory fault when attempting to initialize - * the String. + * the String. */ public abstract class Union extends Structure { private StructField activeField; - StructField biggestField; - - /** Create a Union whose size and alignment will be calculated + + /** Create a Union whose size and alignment will be calculated * automatically. */ protected Union() { } @@ -65,10 +64,10 @@ protected List getFieldOrder() { return list; } - /** Indicates by type which field will be used to write to native memory. + /** Indicates by type which field will be used to write to native memory. * If there are multiple fields of the same type, use {@link * #setType(String)} instead with the field name. - * @throws IllegalArgumentException if the type does not correspond to + * @throws IllegalArgumentException if the type does not correspond to * any declared union field. */ public void setType(Class type) { @@ -82,7 +81,7 @@ public void setType(Class type) { } throw new IllegalArgumentException("No field of type " + type + " in " + this); } - + /** * Indicates which field will be used to write to native memory. * @throws IllegalArgumentException if the name does not correspond to @@ -199,11 +198,11 @@ void writeField(StructField field) { } /** Avoid reading pointer-based fields and structures unless explicitly - * selected. Structures may contain pointer-based fields which can + * selected. Structures may contain pointer-based fields which can * crash the VM if not properly initialized. */ Object readField(StructField field) { - if (field == activeField + if (field == activeField || (!Structure.class.isAssignableFrom(field.type) && !String.class.isAssignableFrom(field.type) && !WString.class.isAssignableFrom(field.type))) { @@ -211,56 +210,12 @@ Object readField(StructField field) { } // Field not accessible // TODO: read structure, to the extent possible; need a "recursive" - // flag to "read" + // flag to "read" to indicate we want to avoid pointer-based fields return null; } - - /** Adjust the size to be the size of the largest element, and ensure - * all fields begin at offset zero. - */ - int calculateSize(boolean force, boolean avoidFFIType) { - int size = super.calculateSize(force, avoidFFIType); - if (size != CALCULATE_SIZE) { - int fsize = 0; - for (Iterator i=fields().values().iterator();i.hasNext();) { - StructField f = (StructField)i.next(); - f.offset = 0; - if (f.size > fsize - // Prefer aggregate types to simple types, since they - // will have more complex packing rules (some platforms - // have specific methods for packing small structs into - // registers, which may not match the packing of bytes - // for a primitive type). - || (f.size == fsize - && Structure.class.isAssignableFrom(f.type))) { - fsize = f.size; - biggestField = f; - } - } - size = calculateAlignedSize(fsize); - if (size > 0) { - // Update native FFI type information, if needed - if (this instanceof ByValue && !avoidFFIType) { - getTypeInfo(); - } - } - } - return size; - } + /** All fields are considered the "first" element. */ protected int getNativeAlignment(Class type, Object value, boolean isFirstElement) { return super.getNativeAlignment(type, value, true); } - - /** Avoid calculating type information until we know our biggest field. - * Return type information for the largest field to ensure all available - * bits are used. - */ - Pointer getTypeInfo() { - if (biggestField == null) { - // Not calculated yet - return null; - } - return super.getTypeInfo(); - } } diff --git a/test/com/sun/jna/StructureTest.java b/test/com/sun/jna/StructureTest.java index 2c4f21c1dc..482e06955d 100644 --- a/test/com/sun/jna/StructureTest.java +++ b/test/com/sun/jna/StructureTest.java @@ -88,7 +88,7 @@ public static class TestAllocStructure extends Structure { public int f2; public int f3; protected List getFieldOrder() { - return Arrays.asList(new String[] { "f0", "f1", "f2", "f3" }); + return Arrays.asList(new String[] { "f0", "f1", "f2", "f3" }); } } @@ -151,7 +151,7 @@ class TestStructure extends Structure { public float f; public double d; protected List getFieldOrder() { - return Arrays.asList(new String[] { "b", "s", "i", "l", "f", "d" }); + return Arrays.asList(new String[] { "b", "s", "i", "l", "f", "d" }); } } Structure s = new TestStructure(); @@ -170,7 +170,7 @@ class TestStructure extends Structure { public float f; public double d; protected List getFieldOrder() { - return Arrays.asList(new String[] { "b", "s", "i", "l", "f", "d" }); + return Arrays.asList(new String[] { "b", "s", "i", "l", "f", "d" }); } } Structure s = new TestStructure(); @@ -195,21 +195,21 @@ public static class TestStructure0 extends FilledStructure { public byte field0 = 0x01; public short field1 = 0x0202; protected List getFieldOrder() { - return Arrays.asList(new String[] { "field0", "field1" }); + return Arrays.asList(new String[] { "field0", "field1" }); } } public static class TestStructure1 extends FilledStructure { public byte field0 = 0x01; public int field1 = 0x02020202; protected List getFieldOrder() { - return Arrays.asList(new String[] { "field0", "field1" }); + return Arrays.asList(new String[] { "field0", "field1" }); } } public static class TestStructure2 extends FilledStructure { public short field0 = 0x0101; public int field1 = 0x02020202; protected List getFieldOrder() { - return Arrays.asList(new String[] { "field0", "field1" }); + return Arrays.asList(new String[] { "field0", "field1" }); } } public static class TestStructure3 extends FilledStructure { @@ -217,7 +217,7 @@ public static class TestStructure3 extends FilledStructure { public short field1 = 0x0202; public int field2 = 0x03030303; protected List getFieldOrder() { - return Arrays.asList(new String[] { "field0", "field1", "field2" }); + return Arrays.asList(new String[] { "field0", "field1", "field2" }); } } public static class TestStructure4 extends FilledStructure { @@ -226,14 +226,14 @@ public static class TestStructure4 extends FilledStructure { public int field2 = 0x03030303; public long field3 = 0x0404040404040404L; protected List getFieldOrder() { - return Arrays.asList(new String[] { "field0", "field1", "field2", "field3" }); + return Arrays.asList(new String[] { "field0", "field1", "field2", "field3" }); } } public static class TestStructure5 extends FilledStructure { public long field0 = 0x0101010101010101L; public byte field1 = 0x02; protected List getFieldOrder() { - return Arrays.asList(new String[] { "field0", "field1" }); + return Arrays.asList(new String[] { "field0", "field1" }); } } public interface SizeTest extends Library { @@ -354,7 +354,7 @@ protected void allocateMemory(int size) { ++allocations; } protected List getFieldOrder() { - return Arrays.asList(new String[] { "x", "y" }); + return Arrays.asList(new String[] { "x", "y" }); } } public void testStructureField() { @@ -362,7 +362,7 @@ class TestStructure extends Structure { public PublicTestStructure s1, s2; public int after; protected List getFieldOrder() { - return Arrays.asList(new String[] { "s1", "s2", "after" }); + return Arrays.asList(new String[] { "s1", "s2", "after" }); } } TestStructure s = new TestStructure(); @@ -386,6 +386,25 @@ protected List getFieldOrder() { s.s2.getPointer()); } + static class TestUnion extends Union { + public int u_int; + public float u_float; + public double u_double; + } + public void testUnionField() { + class TestStructure extends Structure { + public long s_long; + public TestUnion s_union; + public int s_int; + protected List getFieldOrder() { + return Arrays.asList(new String[] { "s_long", "s_union", "s_int" }); + } + } + TestStructure s = new TestStructure(); + assertEquals("Wrong structure size", 24, s.size()); + assertEquals("Wrong union size", 8, s.s_union.size()); + } + public static class NonAllocatingTestStructure extends PublicTestStructure { public NonAllocatingTestStructure() { } public NonAllocatingTestStructure(Pointer p) { super(p); read(); } @@ -394,16 +413,16 @@ protected void allocateMemory(int size) { } } - // TODO: add'l newInstance(Pointer) tests: + // TODO: add'l newInstance(Pointer) tests: // NOTE: ensure structure-by-value respected (no more flag on newjavastructure) - // native call (direct mode) + // native call (direct mode) // getNativeAlignment public void testStructureFieldAvoidsSeparateMemoryAllocation() { class TestStructure extends Structure { public NonAllocatingTestStructure s1; public TestStructure() { } protected List getFieldOrder() { - return Arrays.asList(new String[] { "s1" }); + return Arrays.asList(new String[] { "s1" }); } } TestStructure ts = new TestStructure(); @@ -434,8 +453,8 @@ class TestStructure extends Structure { // initialized array elements public PublicTestStructure[] inner2 = (PublicTestStructure[]) new PublicTestStructure().toArray(2); - protected List getFieldOrder() { - return Arrays.asList(new String[] { "inner", "inner2" }); + protected List getFieldOrder() { + return Arrays.asList(new String[] { "inner", "inner2" }); } } TestStructure s = new TestStructure(); @@ -530,7 +549,7 @@ public TestStructure() { public double[] da = new double[3]; public PublicTestStructure nested; protected List getFieldOrder() { - return Arrays.asList(new String[] { "z", "b", "c", "s", "i", "l", "f", "d", "ba", "ca", "sa", "ia", "la", "fa", "da", "nested" }); + return Arrays.asList(new String[] { "z", "b", "c", "s", "i", "l", "f", "d", "ba", "ca", "sa", "ia", "la", "fa", "da", "nested" }); } } TestStructure s = new TestStructure(); @@ -624,8 +643,8 @@ public void testNativeLongWrite() throws Exception { class TestStructure extends Structure { public int i; public NativeLong l; - protected List getFieldOrder() { - return Arrays.asList(new String[] { "i", "l" }); + protected List getFieldOrder() { + return Arrays.asList(new String[] { "i", "l" }); } } TestStructure s = new TestStructure(); @@ -672,14 +691,14 @@ protected List getFieldOrder() { public static class BadFieldStructure extends Structure { public Object badField; - protected List getFieldOrder() { + protected List getFieldOrder() { return Arrays.asList(new String[] { "badField" }); } } public void testUnsupportedField() { class BadNestedStructure extends Structure { public BadFieldStructure badStruct = new BadFieldStructure(); - protected List getFieldOrder() { + protected List getFieldOrder() { return Arrays.asList(new String[] { "badStruct" }); } } @@ -881,7 +900,7 @@ public void testPreserveStructureByReferenceWithUnchangedPointerOnRead() { assertTrue("Read should preserve structure memory", inner.getPointer() instanceof Memory); } - + public static class TestPointer extends PointerType { } public void testPreservePointerFields() { class TestStructure extends Structure { @@ -925,7 +944,7 @@ protected List getFieldOrder() { assertEquals("String field should not be overwritten", m2, s.getPointer().getPointer(Pointer.SIZE)); } - // Ensure string cacheing doesn't interfere with wrapped structure writes. + // Ensure string cacheing doesn't interfere with wrapped structure writes. public static class StructureFromNative extends Structure { public String s; protected List getFieldOrder() { @@ -938,7 +957,7 @@ public StructureFromNative(Pointer p) { public StructureFromNative() { } } - + public void testInitializeStructureFieldWithStrings() { class ContainingStructure extends Structure { public StructureFromNative inner; @@ -1021,7 +1040,7 @@ protected List getFieldOrder() { } TestStructure s = new TestStructure(); s.field = new PublicTestStructure.ByReference(); - PublicTestStructure.ByReference[] array = + PublicTestStructure.ByReference[] array = (PublicTestStructure.ByReference[])s.field.toArray(2); final int VALUE = -1; array[1].x = VALUE; @@ -1066,13 +1085,13 @@ protected List getFieldOrder() { return Arrays.asList(new String[] { "size", "alignment", "type", "elements" }); } public TestFFIType(Pointer p) { - super(p); + super(p); read(); assertTrue("Test FFIType type not initialized: " + this, this.type != 0); // Force libffi to explicitly calculate the size field of // this FFIType object - int size = Native.initialize_ffi_type(p.peer); + int size = Native.initialize_ffi_type(p.peer); read(); assertEquals("Test FFIType size improperly initialized: " + TestFFIType.this, size, TestFFIType.this.size.intValue()); } @@ -1123,7 +1142,7 @@ class TestStructure extends Structure { public int intField; public PublicTestStructure inner; protected List getFieldOrder() { - return Arrays.asList(new String[] { "intField", "inner" }); + return Arrays.asList(new String[] { "intField", "inner" }); } } TestStructure s = new TestStructure(); @@ -1154,7 +1173,7 @@ public void testNativeMappedWrite() { class TestStructure extends Structure { public ByteByReference ref; protected List getFieldOrder() { - return Arrays.asList(new String[] { "ref" }); + return Arrays.asList(new String[] { "ref" }); } } TestStructure s = new TestStructure(); @@ -1171,7 +1190,7 @@ public void testNativeMappedRead() { class TestStructure extends Structure { public ByteByReference ref; protected List getFieldOrder() { - return Arrays.asList(new String[] { "ref" }); + return Arrays.asList(new String[] { "ref" }); } } TestStructure s = new TestStructure(); @@ -1191,7 +1210,7 @@ protected List getFieldOrder() { public static class ROStructure extends Structure { public final int field; protected List getFieldOrder() { - return Arrays.asList(new String[] { "field" }); + return Arrays.asList(new String[] { "field" }); } { // Initialize in ctor to avoid compiler replacing @@ -1241,7 +1260,7 @@ public void testNativeMappedArrayField() { class TestStructure extends Structure { public NativeLong[] longs = new NativeLong[SIZE]; protected List getFieldOrder() { - return Arrays.asList(new String[] { "longs" }); + return Arrays.asList(new String[] { "longs" }); } } TestStructure s = new TestStructure(); @@ -1277,7 +1296,7 @@ class TestStructure extends Structure { public NativeLong nl = INITIAL; public NativeLong uninitialized; protected List getFieldOrder() { - return Arrays.asList(new String[] { "nl", "uninitialized" }); + return Arrays.asList(new String[] { "nl", "uninitialized" }); } } TestStructure ts = new TestStructure(); @@ -1300,7 +1319,7 @@ public void testThrowErrorOnMissingFieldOrderOnDerivedStructure() { class TestStructure extends Structure { public int f1, f2; protected List getFieldOrder() { - return Arrays.asList(new String[] { "f1", "f2" }); + return Arrays.asList(new String[] { "f1", "f2" }); } } class TestStructure2 extends TestStructure { @@ -1333,7 +1352,7 @@ public void testThrowErrorOnIncorrectFieldOrderCount() { class TestStructure extends Structure { public int f1, f2; protected List getFieldOrder() { - return Arrays.asList(new String[] { "f1", "f2", "f3" }); + return Arrays.asList(new String[] { "f1", "f2", "f3" }); } } try { @@ -1387,7 +1406,7 @@ protected List getFieldOrder() { return list; } } - + TestStructure s = new TestStructure(); assertEquals("Wrong field order", Arrays.asList(ORDER), s.getFieldOrder()); @@ -1444,13 +1463,13 @@ protected List getFieldOrder() { Structure s = new TestStructure(); assertEquals("Wrong type mapper: " + s, mapper, s.getTypeMapper()); } - + public void testWriteWithNullBoxedPrimitives() { class TestStructure extends Structure { public Boolean zfield; public Integer field; protected List getFieldOrder() { - return Arrays.asList(new String[] { "zfield", "field" }); + return Arrays.asList(new String[] { "zfield", "field" }); } } TestStructure s = new TestStructure(); @@ -1465,7 +1484,7 @@ class OtherStructure extends Structure { public int[] second = new int[4]; public Pointer[] third = new Pointer[4]; protected List getFieldOrder() { - return Arrays.asList(new String[] { "first", "second", "third" }); + return Arrays.asList(new String[] { "first", "second", "third" }); } } class TestStructure extends Structure { @@ -1492,7 +1511,7 @@ protected List getFieldOrder() { assertTrue("Equals is not symmetric", s2.equals(s1)); assertTrue("Equals is not transitive", s1.equals(s2) && s2.equals(s3) && s1.equals(s3)); - + } public void testStructureEqualsByValueByReference() { @@ -1501,7 +1520,7 @@ class TestStructure extends Structure { public int[] second = new int[4]; public Pointer[] third = new Pointer[4]; protected List getFieldOrder() { - return Arrays.asList(new String[] { "first", "second", "third" }); + return Arrays.asList(new String[] { "first", "second", "third" }); } } class ByReference extends TestStructure implements Structure.ByReference { } @@ -1518,14 +1537,14 @@ class ByValue extends TestStructure implements Structure.ByValue { } assertTrue("Equals is not symmetric (ByValue)", s3.equals(s1)); assertTrue("Equals is not transitive (ByReference/ByValue)", s1.equals(s2) && s2.equals(s3) && s1.equals(s3)); - + } public void testStructureHashCodeMatchesEqualsTrue() { class TestStructure extends Structure { public int first; protected List getFieldOrder() { - return Arrays.asList(new String[] { "first" }); + return Arrays.asList(new String[] { "first" }); } } TestStructure s1 = new TestStructure(); @@ -1540,7 +1559,7 @@ class TestStructure extends Structure { public byte first; public int second; protected List getFieldOrder() { - return Arrays.asList(new String[] { "first", "second" }); + return Arrays.asList(new String[] { "first", "second" }); } } TestStructure s1 = new TestStructure(); @@ -1559,7 +1578,7 @@ public TestStructureByRef() { } public int unique; public TestStructureByRef s; protected List getFieldOrder() { - return Arrays.asList(new String[] { "unique", "s" }); + return Arrays.asList(new String[] { "unique", "s" }); } } TestStructureByRef s = new TestStructureByRef(); @@ -1592,7 +1611,7 @@ public static class ByReference extends CyclicTestStructure implements Structure public CyclicTestStructure() { } public CyclicTestStructure.ByReference next; protected List getFieldOrder() { - return Arrays.asList(new String[] { "next" }); + return Arrays.asList(new String[] { "next" }); } } public void testCyclicRead() { @@ -1618,7 +1637,7 @@ public void testAvoidMemoryAllocationInPointerCTOR() { class TestStructure extends Structure { public int field; protected List getFieldOrder() { - return Arrays.asList(new String[] { "field" }); + return Arrays.asList(new String[] { "field" }); } public TestStructure(Pointer p) { super(p); @@ -1637,7 +1656,7 @@ class TestStructure extends Structure { public int intField; public byte[] arrayField = new byte[256]; protected List getFieldOrder() { - return Arrays.asList(new String[] { "intField", "arrayField" }); + return Arrays.asList(new String[] { "intField", "arrayField" }); } public TestStructure(Pointer p) { super(p); @@ -1676,7 +1695,7 @@ public TestByReferenceArrayField(Pointer m) { public ByReference[] array = new ByReference[13]; public int value2; protected List getFieldOrder() { - return Arrays.asList(new String[] { "value1", "array", "value2" }); + return Arrays.asList(new String[] { "value1", "array", "value2" }); } public static class ByReference extends TestByReferenceArrayField implements Structure.ByReference { } @@ -1703,7 +1722,7 @@ public void testEquals() { class TestStructure extends Structure { public int field; protected List getFieldOrder() { - return Arrays.asList(new String[] { "field" }); + return Arrays.asList(new String[] { "field" }); } public TestStructure() { } public TestStructure(Pointer p) { super(p); read(); } @@ -1720,7 +1739,7 @@ public void testStructureLayoutCacheing() { class TestStructure extends Structure { public int field; protected List getFieldOrder() { - return Arrays.asList(new String[] { "field" }); + return Arrays.asList(new String[] { "field" }); } } Structure ts = new TestStructure(); ts.ensureAllocated(); @@ -1728,12 +1747,12 @@ protected List getFieldOrder() { assertSame("Structure layout not cached", ts.fields(), ts2.fields()); } - + public void testStructureLayoutVariableNoCache() { class TestStructure extends Structure { public byte[] variable; protected List getFieldOrder() { - return Arrays.asList(new String[] { "variable" }); + return Arrays.asList(new String[] { "variable" }); } public TestStructure(int size) { this.variable = new byte[size]; @@ -1767,7 +1786,7 @@ public Object toNative(Object value, ToNativeContext c) { class TestStructure extends Structure { public boolean field; protected List getFieldOrder() { - return Arrays.asList(new String[] { "field" }); + return Arrays.asList(new String[] { "field" }); } public TestStructure() { super(m); @@ -1788,7 +1807,7 @@ class TestStructure extends Structure { public byte first; public int second; protected List getFieldOrder() { - return Arrays.asList(new String[] { "first", "second" }); + return Arrays.asList(new String[] { "first", "second" }); } public TestStructure() { setAlignType(ALIGN_NONE); @@ -1851,7 +1870,7 @@ public TestStructure() { super(mapper); } protected List getFieldOrder() { - return Arrays.asList(new String[] { "b", "s", "p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7" }); + return Arrays.asList(new String[] { "b", "s", "p0", "p1", "p2", "p3", "p4", "p5", "p6", "p7" }); } } Structure s = new TestStructure(); diff --git a/test/com/sun/jna/UnionTest.java b/test/com/sun/jna/UnionTest.java index 579c26c646..6f2d0d0c70 100644 --- a/test/com/sun/jna/UnionTest.java +++ b/test/com/sun/jna/UnionTest.java @@ -1,14 +1,14 @@ /* Copyright (c) 2007 Timothy Wall, All Rights Reserved - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * Lesser General Public License for more details. */ package com.sun.jna; @@ -23,22 +23,22 @@ public class UnionTest extends TestCase { public static class TestStructure extends Structure { public String value; protected List getFieldOrder() { - return Arrays.asList(new String[] { "value" }); + return Arrays.asList(new String[] { "value" }); } } - + public static class BigTestStructure extends Structure { public long field1; public long field2; protected List getFieldOrder() { - return Arrays.asList(new String[] { "field1", "field2" }); + return Arrays.asList(new String[] { "field1", "field2" }); } } - + public static class IntStructure extends Structure { public int value; protected List getFieldOrder() { - return Arrays.asList(new String[] { "value" }); + return Arrays.asList(new String[] { "value" }); } } @@ -59,7 +59,7 @@ public static class SizedUnion extends Union { public WString wstring; public Pointer pointer; } - + public static class StructUnion extends Union { public int intField; public TestStructure testStruct; @@ -69,31 +69,33 @@ public static class StructUnion extends Union { public void testCalculateSize() { Union u = new SizedUnion(); + assertEquals("Wrong union size: " + u, 16, u.size()); assertEquals("Union should be size of largest field", new BigTestStructure().size(), u.size()); } public void testFieldOffsets() { StructUnion u = new StructUnion(); + assertEquals("Wrong union size: " + u, Pointer.SIZE, u.size()); u.setType(u.testStruct.getClass()); u.write(); - assertEquals("Wrong struct member base address", + assertEquals("Wrong struct member base address", u.getPointer(), u.testStruct.getPointer()); u.setType(u.intStruct.getClass()); u.write(); - assertEquals("Wrong struct member base address (2)", + assertEquals("Wrong struct member base address (2)", u.getPointer(), u.intStruct.getPointer()); } public void testWriteUnion() { SizedUnion u = new SizedUnion(); - final int VALUE = 0x12345678; + final int VALUE = 0x12345678; u.intField = VALUE; u.setType(int.class); u.write(); assertEquals("Wrong value written", VALUE, u.getPointer().getInt(0)); } - + public void testReadUnion() { SizedUnion u = new SizedUnion(); final int VALUE = 0x12345678; @@ -109,11 +111,12 @@ public void testReadUnion() { assertNull("Unselected String should be null", u.string); assertNull("Unselected WString should be null", u.wstring); } - + public void testWriteTypedUnion() { final int VALUE = 0x12345678; // write an instance of a direct union class to memory StructUnion u = new StructUnion(); + assertEquals("Wrong union size: " + u, Pointer.SIZE, u.size()); IntStructure intStruct = new IntStructure(); intStruct.value = VALUE; u.setTypedValue(intStruct); @@ -138,6 +141,7 @@ public void callback() { public void testReadTypedUnion() { StructUnion u = new StructUnion(); + assertEquals("Wrong union size: " + u, Pointer.SIZE, u.size()); final int VALUE = 0x12345678; u.getPointer().setInt(0, VALUE); assertEquals("int structure not read properly", VALUE, ((IntStructure) u.getTypedValue(IntStructure.class)).value); @@ -145,29 +149,34 @@ public void testReadTypedUnion() { public void testReadTypeInfo() { SizedUnion u = new SizedUnion(); + assertEquals("Wrong union size: " + u, 16, u.size()); + assertNotNull("Type information is missing for union field of type " + BigTestStructure.class, Structure.getTypeInfo(BigTestStructure.class)); + assertNotNull("Type information is missing for union instance", u.getTypeInfo()); if (Native.POINTER_SIZE == 4) { - assertEquals("Type size should be that of longest field if no field active", + assertEquals("Type size should be that of largest field if no field is active", Structure.getTypeInfo(BigTestStructure.class).getInt(0), u.getTypeInfo().getInt(0)); } else { - assertEquals("Type size should be that of longest field if no field active", + assertEquals("Type size should be that of largest field if no field is active", Structure.getTypeInfo(BigTestStructure.class).getLong(0), u.getTypeInfo().getLong(0)); } u.setType(int.class); + assertNotNull("Type information is missing for union field of type " + BigTestStructure.class, Structure.getTypeInfo(BigTestStructure.class)); + assertNotNull("Type information is missing for union instance after type set", u.getTypeInfo()); if (Native.POINTER_SIZE == 4) { - assertEquals("Type size should be that of longest field if field active", + assertEquals("Type size should be that of largest field if any field is active", Structure.getTypeInfo(BigTestStructure.class).getInt(0), u.getTypeInfo().getInt(0)); } else { - assertEquals("Type size should be that of longest field if field active", + assertEquals("Type size should be that of largest field if any field is active", Structure.getTypeInfo(BigTestStructure.class).getLong(0), u.getTypeInfo().getLong(0)); } } - + public void testArraysInUnion() { class TestUnion extends Union { public byte[] bytes = new byte[16]; @@ -175,6 +184,7 @@ class TestUnion extends Union { public int[] ints = new int[4]; } Union u = new TestUnion(); + assertEquals("Wrong union size: " + u, 16, u.size()); u.setType(byte[].class); u.setType(short[].class); u.setType(int[].class); @@ -183,9 +193,10 @@ class TestUnion extends Union { public void testDuplicateFieldTypes() { class TestUnion extends Union { public int field1; - public int field2; + public int field2; } TestUnion u = new TestUnion(); + assertEquals("Wrong union size: " + u, 4, u.size()); u.setType("field1"); u.field1 = 42; u.write(); From e35977776d217f47ce979e74f37b540fc7a59c53 Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Wed, 1 May 2013 15:29:50 -0400 Subject: [PATCH 13/15] symlink creation now broken on osx 10.8.3 --- .../com/sun/jna/platform/FileUtilsTest.java | 19 +++++++------------ test/com/sun/jna/CallbacksTest.java | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/contrib/platform/test/com/sun/jna/platform/FileUtilsTest.java b/contrib/platform/test/com/sun/jna/platform/FileUtilsTest.java index 114200e10e..a08c2a9998 100644 --- a/contrib/platform/test/com/sun/jna/platform/FileUtilsTest.java +++ b/contrib/platform/test/com/sun/jna/platform/FileUtilsTest.java @@ -28,7 +28,7 @@ public void testMoveToTrash() throws Exception { File tmpdir = new File(System.getProperty("java.io.tmpdir")); File file = File.createTempFile(getName(), ".tmp", tmpdir); try { - assertTrue("File should exist", file.exists()); + assertTrue("Original source file missing: " + file, file.exists()); try { utils.moveToTrash(new File[] { file }); } @@ -38,9 +38,7 @@ public void testMoveToTrash() throws Exception { assertFalse("File still exists after move to trash: " + file, file.exists()); } finally { - if (file.exists()) { - file.delete(); - } + file.delete(); } } @@ -55,9 +53,10 @@ public void testMoveSymlinkToTrash() throws Exception { File tmpdir = new File(System.getProperty("java.io.tmpdir")); File file = File.createTempFile(getName(), ".tmp", tmpdir); File symlink = new File(tmpdir, file.getName() + ".link"); - Runtime.getRuntime().exec(new String[] { "ln", "-s", file.getAbsolutePath(), symlink.getAbsolutePath() }); try { - assertTrue("File should exist", symlink.exists()); + Runtime.getRuntime().exec(new String[] { "ln", "-s", file.getAbsolutePath(), symlink.getAbsolutePath() }); + assertTrue("Original source file missing: " + file, file.exists()); + assertTrue("Symlink creation failed (missing): " + symlink, symlink.exists()); try { utils.moveToTrash(new File[] { symlink }); } @@ -68,12 +67,8 @@ public void testMoveSymlinkToTrash() throws Exception { assertTrue("Original file should still exist after move to trash: " + file, file.exists()); } finally { - if (symlink.exists()) { - symlink.delete(); - } - if (file.exists()) { - file.delete(); - } + symlink.delete(); + file.delete(); } } diff --git a/test/com/sun/jna/CallbacksTest.java b/test/com/sun/jna/CallbacksTest.java index 612751b950..306141149e 100644 --- a/test/com/sun/jna/CallbacksTest.java +++ b/test/com/sun/jna/CallbacksTest.java @@ -1088,7 +1088,7 @@ public void callback() { 1, threads.size()); } - // Thread object is never GC'd on linux-amd64 and (sometimes) win32-amd64 + // Thread object is never GC'd on linux-amd64 and darwin-amd64 (w/openjdk7) public void testAttachedThreadCleanupOnExit() throws Exception { final Set threads = new HashSet(); final int[] called = { 0 }; From 198cb1f9df7f9167c26fe1201e010078494e0908 Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Mon, 6 May 2013 08:25:36 -0400 Subject: [PATCH 14/15] fix file permissions --- .classpath | 0 .project | 0 CHANGES.md | 0 LICENSE | 0 OTHERS | 0 contrib/README | 0 contrib/balloontips/README | 0 src/com/sun/jna/Version.java | 0 test/com/sun/jna/WebStartTest.java | 1 + 9 files changed, 1 insertion(+) mode change 100755 => 100644 .classpath mode change 100755 => 100644 .project mode change 100755 => 100644 CHANGES.md mode change 100755 => 100644 LICENSE mode change 100755 => 100644 OTHERS mode change 100755 => 100644 contrib/README mode change 100755 => 100644 contrib/balloontips/README mode change 100755 => 100644 src/com/sun/jna/Version.java diff --git a/.classpath b/.classpath old mode 100755 new mode 100644 diff --git a/.project b/.project old mode 100755 new mode 100644 diff --git a/CHANGES.md b/CHANGES.md old mode 100755 new mode 100644 diff --git a/LICENSE b/LICENSE old mode 100755 new mode 100644 diff --git a/OTHERS b/OTHERS old mode 100755 new mode 100644 diff --git a/contrib/README b/contrib/README old mode 100755 new mode 100644 diff --git a/contrib/balloontips/README b/contrib/balloontips/README old mode 100755 new mode 100644 diff --git a/src/com/sun/jna/Version.java b/src/com/sun/jna/Version.java old mode 100755 new mode 100644 diff --git a/test/com/sun/jna/WebStartTest.java b/test/com/sun/jna/WebStartTest.java index 0777f6e1ca..899b2cff81 100644 --- a/test/com/sun/jna/WebStartTest.java +++ b/test/com/sun/jna/WebStartTest.java @@ -149,6 +149,7 @@ private void runTestUnderWebStart(String testClass, String testMethod) throws Ex String path = findJWS(); String[] cmd = { path, + Platform.isWindows() ? "-J-Ddummy" : (Platform.is64Bit() ? "-J-d64" : "-J-d32"), "-Xnosplash", "-wait", jnlp.toURI().toURL().toString(), From b1aa24d911c75e3646c59dea58b685625f108e5d Mon Sep 17 00:00:00 2001 From: Timothy Wall Date: Mon, 6 May 2013 09:04:02 -0400 Subject: [PATCH 15/15] add test for loading from path with unicode characters (fails on XP, at least) --- test/com/sun/jna/JNAUnloadTest.java | 22 ++++++++++++++++++++-- test/com/sun/jna/LastErrorTest.java | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/test/com/sun/jna/JNAUnloadTest.java b/test/com/sun/jna/JNAUnloadTest.java index 1e26357427..6250cb03f0 100644 --- a/test/com/sun/jna/JNAUnloadTest.java +++ b/test/com/sun/jna/JNAUnloadTest.java @@ -18,6 +18,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.util.Properties; import junit.framework.TestCase; @@ -99,7 +100,7 @@ public void testAvoidResourcePathLoading() throws Exception { } } - // Fails under clover + // GC Fails under clover public void testLoadAndUnloadFromJar() throws Exception { ClassLoader loader = new TestLoader(true); Class cls = Class.forName("com.sun.jna.Native", true, loader); @@ -153,7 +154,7 @@ public void testLoadAndUnloadFromJar() throws Exception { } } - // Fails under clover and OpenJDK(linux/ppc) + // GC Fails under clover and OpenJDK(linux/ppc) public void testLoadAndUnloadFromResourcePath() throws Exception { ClassLoader loader = new TestLoader(false); Class cls = Class.forName("com.sun.jna.Native", true, loader); @@ -205,6 +206,23 @@ public void testLoadAndUnloadFromResourcePath() throws Exception { } } + public void testLoadFromUnicodePath() throws Exception { + final String UNICODE = getName() + "-\u0444\u043b\u0441\u0432\u0443"; + File tmpdir = Native.getTempDir(); + File unicodeDir = new File(tmpdir, UNICODE); + unicodeDir.mkdirs(); + Properties props = System.getProperties(); + try { + System.setProperty("jna.tmpdir", unicodeDir.getAbsolutePath()); + ClassLoader loader = new TestLoader(true); + Class cls = Class.forName("com.sun.jna.Native", true, loader); + assertEquals("Wrong class loader", loader, cls.getClassLoader()); + } + finally { + System.setProperties(props); + } + } + public static void main(String[] args) { junit.textui.TestRunner.run(JNAUnloadTest.class); } diff --git a/test/com/sun/jna/LastErrorTest.java b/test/com/sun/jna/LastErrorTest.java index ff7692d975..a900428308 100644 --- a/test/com/sun/jna/LastErrorTest.java +++ b/test/com/sun/jna/LastErrorTest.java @@ -102,7 +102,7 @@ public void testThrowLastErrorDirect() { } catch(LastErrorException e) { assertEquals("Exception should contain error code", ERROR, e.getErrorCode()); - assertTrue("Exception should include error message: " + e.getMessage(), e.getMessage().length() > 10); + assertTrue("Exception should include error message: " + e.getMessage(), e.getMessage().length() > 0); } }