11package io .github .treesitter .jtreesitter ;
22
3+ import java .lang .foreign .*;
34import static io .github .treesitter .jtreesitter .internal .TreeSitter .*;
45
56import io .github .treesitter .jtreesitter .internal .TreeSitter ;
6- import java .lang .foreign .Arena ;
7- import java .lang .foreign .MemorySegment ;
7+
88import org .jspecify .annotations .NullMarked ;
99import 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