Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for a custom SymbolProvider in NativeLibrary & Library #1490

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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));
}
}