Skip to content

Commit 078a1c2

Browse files
feat(language): add static load function
Closes #23
1 parent 77449c1 commit 078a1c2

File tree

1 file changed

+40
-2
lines changed

1 file changed

+40
-2
lines changed

src/main/java/io/github/treesitter/jtreesitter/Language.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package io.github.treesitter.jtreesitter;
22

3+
import java.lang.foreign.*;
34
import static io.github.treesitter.jtreesitter.internal.TreeSitter.*;
45

56
import io.github.treesitter.jtreesitter.internal.TreeSitter;
6-
import java.lang.foreign.Arena;
7-
import java.lang.foreign.MemorySegment;
7+
88
import org.jspecify.annotations.NullMarked;
99
import org.jspecify.annotations.Nullable;
1010

@@ -22,6 +22,13 @@ public final class Language {
2222
/** The earliest ABI version that is supported by the current version of the library. */
2323
public static final @Unsigned int MIN_COMPATIBLE_LANGUAGE_VERSION = TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION();
2424

25+
private static final ValueLayout VOID_PTR =
26+
ValueLayout.ADDRESS.withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, ValueLayout.JAVA_BYTE));
27+
28+
private static final FunctionDescriptor FUNC_DESC = FunctionDescriptor.of(VOID_PTR);
29+
30+
private static final Linker LINKER = Linker.nativeLinker();
31+
2532
private final MemorySegment self;
2633

2734
private final @Unsigned int version;
@@ -43,6 +50,37 @@ public Language(MemorySegment self) throws IllegalArgumentException {
4350
}
4451
}
4552

53+
private static UnsatisfiedLinkError unresolved(String name) {
54+
return new UnsatisfiedLinkError("Unresolved symbol: %s".formatted(name));
55+
}
56+
57+
/**
58+
* Load a language by looking for its function in the given symbols.
59+
*
60+
* <h4 id="load-example">Example</h4>
61+
*
62+
* <p>{@snippet lang="java" :
63+
* Language language;
64+
* try (Arena arena = Arena.ofConfined()) {
65+
* String library = System.mapLibraryName("tree-sitter-java");
66+
* SymbolLookup symbols = SymbolLookup.libraryLookup(library, arena);
67+
* language = Language.load(symbols, "tree_sitter_java");
68+
* }
69+
* }
70+
*
71+
* @throws RuntimeException If the language could not be loaded.
72+
*/
73+
// TODO: deprecate when the bindings are generated by the CLI
74+
public static Language load(SymbolLookup symbols, String language) throws RuntimeException {
75+
var address = symbols.find(language).orElseThrow(() -> unresolved(language));
76+
try {
77+
var function = LINKER.downcallHandle(address, FUNC_DESC);
78+
return new Language((MemorySegment) function.invokeExact());
79+
} catch (Throwable e) {
80+
throw new RuntimeException("Failed to load %s".formatted(language), e);
81+
}
82+
}
83+
4684
MemorySegment segment() {
4785
return self;
4886
}

0 commit comments

Comments
 (0)