Skip to content

Commit

Permalink
Adds support for a custom SymbolProvider in NativeLibrary & Library
Browse files Browse the repository at this point in the history
  • Loading branch information
soywiz committed Dec 18, 2022
1 parent 49d80ee commit 5a6a9f2
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Features
* [#1471](https://github.com/java-native-access/jna/pull/1471): Add `c.s.j.p.win32.Advapi32Util#isCurrentProcessElevated` and associated Types - [@dbwiddis](https://github.com/dbwiddis).
* [#1474](https://github.com/java-native-access/jna/pull/1474): Add `c.s.j.p.win32.WbemCli#IWbemClassObject.IWbemQualifierSet`, `IWbemServices.GetObject`, `IWbemContext.SetValue` and associated methods - [@rchateauneu](https://github.com/rchateauneu).
* [#1482](https://github.com/java-native-access/jna/pull/1482): Add multilingual support of `Kernel32Util.formatMessage` - [@overpathz](https://github.com/overpathz).
* [#1490](https://github.com/java-native-access/jna/pull/1490): Adds support for a custom `SymbolProvider` in `NativeLibrary` & `Library` - [@soywiz](https://github.com/soywiz).

Bug Fixes
---------
Expand Down
5 changes: 5 additions & 0 deletions src/com/sun/jna/Library.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ public interface Library {
*/
String OPTION_CLASSLOADER = "classloader";

/**
* Supports a custom symbol provider for the NativeLibrary (see {@link SymbolProvider})
*/
String OPTION_SYMBOL_PROVIDER = "symbol-provider";

static class Handler implements InvocationHandler {

static final Method OBJECT_TOSTRING;
Expand Down
18 changes: 16 additions & 2 deletions src/com/sun/jna/NativeLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,20 @@
public class NativeLibrary implements Closeable {

private static final Logger LOG = Logger.getLogger(NativeLibrary.class.getName());
private final static Level DEBUG_LOAD_LEVEL = DEBUG_LOAD ? Level.INFO : Level.FINE;
private static final Level DEBUG_LOAD_LEVEL = DEBUG_LOAD ? Level.INFO : Level.FINE;
private static final SymbolProvider NATIVE_SYMBOL_PROVIDER = new SymbolProvider() {
@Override
public long getSymbolAddress(long handle, String name, SymbolProvider parent) {
return Native.findSymbol(handle, name);
}
};

private Cleaner.Cleanable cleanable;
private long handle;
private final String libraryName;
private final String libraryPath;
private final Map<String, Function> functions = new HashMap<String, Function>();
private final SymbolProvider symbolProvider;
final int callFlags;
private String encoding;
final Map<String, ?> options;
Expand Down Expand Up @@ -123,6 +130,13 @@ private NativeLibrary(String libraryName, String libraryPath, long handle, Map<S
this.callFlags = callingConvention;
this.options = options;
this.encoding = (String)options.get(Library.OPTION_STRING_ENCODING);
SymbolProvider optionSymbolProvider = (SymbolProvider)options.get(Library.OPTION_SYMBOL_PROVIDER);
if (optionSymbolProvider == null) {
this.symbolProvider = NATIVE_SYMBOL_PROVIDER;
} else {
this.symbolProvider = optionSymbolProvider;
}

if (this.encoding == null) {
this.encoding = Native.getDefaultStringEncoding();
}
Expand Down Expand Up @@ -636,7 +650,7 @@ long getSymbolAddress(String name) {
if (handle == 0) {
throw new UnsatisfiedLinkError("Library has been unloaded");
}
return Native.findSymbol(handle, name);
return this.symbolProvider.getSymbolAddress(handle, name, NATIVE_SYMBOL_PROVIDER);
}

@Override
Expand Down
43 changes: 43 additions & 0 deletions src/com/sun/jna/SymbolProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* Copyright (c) 2022 Carlos Ballesteros, All Rights Reserved
*
* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna;

/**
* Interface to define a custom symbol provider.
*
* This can be used for method hooking, or special
* classes like direct mapping the Win32 OpenGL.
*/
public interface SymbolProvider {
/**
* Gets the address of a symbol by its name and the handle of the library.
*
* @param handle Handle of the original library
* @param name Name of the symbol to load
* @param parent Parent symbol provider
*
* @return Address of the symbol, typically a function.
*/
long getSymbolAddress(long handle, String name, SymbolProvider parent);
}
104 changes: 104 additions & 0 deletions test/com/sun/jna/NativeCustomSymbolProviderTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/* Copyright (c) 2022 Carlos Ballesteros, All Rights Reserved
*
* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna;

import junit.framework.TestCase;

import java.util.Collections;

public class NativeCustomSymbolProviderTest extends TestCase implements Paths {
public static void main(java.lang.String[] argList) {
junit.textui.TestRunner.run(NativeCustomSymbolProviderTest.class);
}

interface MathInterfaceWithSymbolProvider extends Library {

double sin(double x);
double cos(double x);

}

static class MathLibraryWithSymbolProvider {

public static native double sin(double x);
public static native double cos(double x);

static {
Native.register(MathLibraryWithSymbolProvider.class, NativeLibrary.getInstance(Platform.MATH_LIBRARY_NAME, Collections.singletonMap(
Library.OPTION_SYMBOL_PROVIDER,
new SymbolProvider() {
@Override
public long getSymbolAddress(long handle, String name, SymbolProvider parent) {
if (name.equals("sin")) {
return parent.getSymbolAddress(handle, "cos", null);
} else {
return parent.getSymbolAddress(handle, "sin", null);
}
}
}
)));
}
}

MathInterfaceWithSymbolProvider lib;
MathInterfaceWithSymbolProvider libCustom;

@Override
protected void setUp() {
lib = Native.load(Platform.MATH_LIBRARY_NAME, MathInterfaceWithSymbolProvider.class);
libCustom = Native.load(
Platform.MATH_LIBRARY_NAME,
MathInterfaceWithSymbolProvider.class, Collections.singletonMap(
Library.OPTION_SYMBOL_PROVIDER,
new SymbolProvider() {
@Override
public long getSymbolAddress(long handle, String name, SymbolProvider parent) {
if (name.equals("sin")) {
return parent.getSymbolAddress(handle, "cos", null);
} else {
return parent.getSymbolAddress(handle, "sin", null);
}
}
}
)
);
}

@Override
protected void tearDown() {
lib = null;
libCustom = null;
}


public void testDirectMappingSymbolProvider() {
assertEquals(lib.cos(0.0), MathLibraryWithSymbolProvider.sin(0.0));
assertEquals(lib.sin(0.0), MathLibraryWithSymbolProvider.cos(0.0));
}

public void testInterfaceCustomSymbolProvider() {
assertEquals(lib.cos(0.0), libCustom.sin(0.0));
assertEquals(lib.sin(0.0), libCustom.cos(0.0));
}
}

0 comments on commit 5a6a9f2

Please sign in to comment.