Skip to content

Commit 6ae5953

Browse files
odenixObserverOfTime
authored andcommitted
feat: support custom SymbolLookup for the library
Enable clients to customize the SymbolLookup used for the tree-sitter native library. For example, clients can use this capability to embed the tree-sitter native library in their JAR, which is a common way to solve the native library distribution problem.
1 parent b9b0a31 commit 6ae5953

File tree

5 files changed

+88
-17
lines changed

5 files changed

+88
-17
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ Java bindings to the [tree-sitter] parsing library.
1010

1111
- Install JDK 22 and set `JAVA_HOME` to it
1212
- Download [jextract] and add it to your `PATH`
13+
- Install the `tree-sitter` & `tree-sitter-java` libraries
1314

1415
```bash
1516
git clone https://github.com/tree-sitter/java-tree-sitter
1617
cd java-tree-sitter
17-
git submodule init
18+
git submodule update --init
1819
mvn test
1920
```
2021

scripts/TreeSitter_java.patch

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,14 @@
11
--- a/generated-sources/jextract/io/github/treesitter/jtreesitter/internal/TreeSitter.java
22
+++ b/generated-sources/jextract/io/github/treesitter/jtreesitter/internal/TreeSitter.java
3-
@@ -55,9 +55,16 @@ public class TreeSitter {
3+
@@ -55,9 +55,7 @@
44
};
55
}
6-
6+
77
- static final SymbolLookup SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName("tree-sitter"), LIBRARY_ARENA)
88
- .or(SymbolLookup.loaderLookup())
99
- .or(Linker.nativeLinker().defaultLookup());
10-
+ static final SymbolLookup SYMBOL_LOOKUP = findLibrary().or(Linker.nativeLinker().defaultLookup());
11-
+
12-
+ private static final SymbolLookup findLibrary() {
13-
+ try {
14-
+ String library = System.mapLibraryName("tree-sitter");
15-
+ return SymbolLookup.libraryLookup(library, LIBRARY_ARENA);
16-
+ } catch (IllegalArgumentException e) {
17-
+ return SymbolLookup.loaderLookup();
18-
+ }
19-
+ }
20-
10+
+ static final SymbolLookup SYMBOL_LOOKUP = ChainedLibraryLookup.INSTANCE.get(LIBRARY_ARENA);
11+
2112
public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN;
2213
public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE;
2314
@@ -8599,4 +8606,8 @@ public class TreeSitter {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.github.treesitter.jtreesitter;
2+
3+
import java.lang.foreign.Arena;
4+
import java.lang.foreign.SymbolLookup;
5+
6+
/**
7+
* An interface implemented by clients that wish to customize the {@link SymbolLookup}
8+
* used for the tree-sitter native library. Implementations must be registered
9+
* by listing their fully qualified class name in a resource file named
10+
* {@code META-INF/services/io.github.treesitter.jtreesitter.NativeLibraryLookup}.
11+
*
12+
* @see java.util.ServiceLoader
13+
*/
14+
@FunctionalInterface
15+
public interface NativeLibraryLookup {
16+
/**
17+
* Get the {@link SymbolLookup} to be used for the tree-sitter native library.
18+
*
19+
* @param arena The arena that will manage the native memory.
20+
*/
21+
SymbolLookup get(Arena arena);
22+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.github.treesitter.jtreesitter.internal;
2+
3+
import io.github.treesitter.jtreesitter.NativeLibraryLookup;
4+
import java.lang.foreign.Arena;
5+
import java.lang.foreign.Linker;
6+
import java.lang.foreign.SymbolLookup;
7+
import java.util.Optional;
8+
import java.util.ServiceLoader;
9+
10+
@SuppressWarnings("unused")
11+
final class ChainedLibraryLookup implements NativeLibraryLookup {
12+
private ChainedLibraryLookup() {}
13+
14+
static ChainedLibraryLookup INSTANCE = new ChainedLibraryLookup();
15+
16+
@Override
17+
public SymbolLookup get(Arena arena) {
18+
var serviceLoader = ServiceLoader.load(NativeLibraryLookup.class);
19+
// NOTE: can't use _ because of palantir/palantir-java-format#934
20+
SymbolLookup lookup = (name) -> Optional.empty();
21+
for (var libraryLookup : serviceLoader) {
22+
lookup = lookup.or(libraryLookup.get(arena));
23+
}
24+
return lookup.or(findLibrary(arena)).or(Linker.nativeLinker().defaultLookup());
25+
}
26+
27+
private static SymbolLookup findLibrary(Arena arena) {
28+
try {
29+
var library = System.mapLibraryName("tree-sitter");
30+
return SymbolLookup.libraryLookup(library, arena);
31+
} catch (IllegalArgumentException e) {
32+
return SymbolLookup.loaderLookup();
33+
}
34+
}
35+
}

src/main/java/io/github/treesitter/jtreesitter/package-info.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
* <li>Shared libraries for languages</li>
1616
* </ul>
1717
*
18-
* <em>The shared libraries must be installed system-wide or in {@systemProperty java.library.path}</em>
19-
*
2018
* <h2 id="usage">Basic Usage</h2>
2119
*
2220
* {@snippet lang = java:
@@ -31,6 +29,30 @@
3129
* }
3230
*}
3331
*
34-
* @see <a href="https://github.com/openjdk/jextract/blob/master/doc/GUIDE.md#library-loading">Library Loading</a>
32+
* <h2 id="libraries">Library Loading</h2>
33+
*
34+
* There are three ways to load the shared libraries:
35+
*
36+
* <ol>
37+
* <li>
38+
* The libraries can be installed in the OS-specific library search path or in
39+
* {@systemProperty java.library.path}. The search path can be amended using the
40+
* {@code LD_LIBRARY_PATH} environment variable on Linux, {@code DYLD_LIBRARY_PATH}
41+
* on macOS, or {@code PATH} on Windows. The libraries will be loaded automatically by
42+
* {@link java.lang.foreign.SymbolLookup#libraryLookup(String, java.lang.foreign.Arena)
43+
* SymbolLookup.libraryLookup(String, Arena)}.
44+
* </li>
45+
* <li>
46+
* The libraries can be loaded manually by calling
47+
* {@link java.lang.System#loadLibrary(String) System.loadLibrary(String)},
48+
* if the library is installed in {@systemProperty java.library.path},
49+
* or {@link java.lang.System#load(String) System.load(String)}.
50+
* </li>
51+
* <li>
52+
* The libraries can be loaded manually by registering a custom implementation of
53+
* {@link io.github.treesitter.jtreesitter.NativeLibraryLookup NativeLibraryLookup}.
54+
* This can be used, for example, to load libraries from inside a JAR file.
55+
* </li>
56+
* </ol>
3557
*/
3658
package io.github.treesitter.jtreesitter;

0 commit comments

Comments
 (0)