Skip to content
This repository was archived by the owner on Oct 18, 2024. It is now read-only.

Commit 1f0eb75

Browse files
committed
fix: automatically generate method headers and signature defs for native methods
1 parent a0e3ea8 commit 1f0eb75

File tree

17 files changed

+972
-35
lines changed

17 files changed

+972
-35
lines changed

android-tree-sitter/build.gradle.kts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,23 @@ plugins {
2323

2424
description = "Android Java bindings for Tree Sitter."
2525

26+
val nativeHeadersDir =
27+
project.layout.buildDirectory.dir("generated/native_headers")
28+
2629
android {
2730
namespace = "com.itsaky.androidide.treesitter"
31+
32+
defaultConfig {
33+
externalNativeBuild {
34+
cmake {
35+
arguments += "-DAUTOGEN_HEADERS=${nativeHeadersDir.get().asFile.absolutePath}"
36+
}
37+
}
38+
}
39+
}
40+
41+
tasks.withType(JavaCompile::class.java).configureEach {
42+
options.compilerArgs.add("-AnativeHeaders.outDir=${nativeHeadersDir.get().asFile.absolutePath}")
2843
}
2944

3045
dependencies {

android-tree-sitter/src/main/cpp/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ include(../../../../cmake/common-config.cmake)
2929
# headers for those are located in the tree-sitter sources
3030
include_directories("${TS_DIR}/lib/src")
3131

32+
# Auto-generated headers
33+
include_directories(${AUTOGEN_HEADERS})
34+
3235
# add android-tree-sitter library
3336
add_library(android-tree-sitter SHARED
3437

android-tree-sitter/src/main/cpp/ts.cc

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@
1515
* along with android-tree-sitter. If not, see <https://www.gnu.org/licenses/>.
1616
*/
1717

18+
#include <array>
19+
20+
#include "utils/jni_macros.h"
1821
#include "utils/ts_obj_utils.h"
22+
#include "utils/ts_log.h"
23+
#include "ts_meta.h"
24+
#include "ts_meta_sigs.h"
1925

2026
#if defined(__ANDROID__)
2127
static jint JNI_VERSION = JNI_VERSION_1_6;
@@ -41,14 +47,30 @@ void JNI_OnUnload(JavaVM *vm, void *reserved) {
4147
}
4248

4349

44-
extern "C" JNIEXPORT jint JNICALL
45-
Java_com_itsaky_androidide_treesitter_TreeSitter_getLanguageVersion
46-
(JNIEnv *env, jclass self) {
50+
JNIEXPORT jint JNICALL ats_language_version(JNIEnv *env, jclass self) {
4751
return (jint) TREE_SITTER_LANGUAGE_VERSION;
4852
}
4953

50-
extern "C" JNIEXPORT jint JNICALL
51-
Java_com_itsaky_androidide_treesitter_TreeSitter_getMinimumCompatibleLanguageVersion
52-
(JNIEnv *env, jclass self) {
54+
JNIEXPORT jint JNICALL
55+
ats_min_compatible_language_version(JNIEnv *env, jclass self) {
5356
return (jint) TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION;
57+
}
58+
59+
static JNINativeMethod gMethods[] = {
60+
MAKE_JNI_METHOD(TS_TREESITTER_GETLANGUAGEVERSION_NAME,
61+
TS_TREESITTER_GETLANGUAGEVERSION_SIG,
62+
ats_language_version),
63+
MAKE_JNI_METHOD(TS_TREESITTER_GETMINIMUMCOMPATIBLELANGUAGEVERSION_NAME,
64+
TS_TREESITTER_GETMINIMUMCOMPATIBLELANGUAGEVERSION_SIG,
65+
ats_min_compatible_language_version)
66+
};
67+
68+
extern "C"
69+
JNIEXPORT void JNICALL
70+
Java_com_itsaky_androidide_treesitter_TreeSitter_00024Native_registerNatives(
71+
JNIEnv *env,
72+
jclass clazz) {
73+
74+
auto result = env->RegisterNatives(clazz, gMethods, 2);
75+
LOGD("TreeSitter", "RegisterNatives: %d", result);
5476
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* This file is part of android-tree-sitter.
3+
*
4+
* android-tree-sitter library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* android-tree-sitter library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with android-tree-sitter. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
#ifndef ATS_JNI_MACROS_H
19+
#define ATS_JNI_MACROS_H
20+
21+
#include <jni.h>
22+
23+
#define MAKE_JNI_METHOD(_name, _sig, _func) (JNINativeMethod) { \
24+
.name = _name, \
25+
.signature = _sig, \
26+
.fnPtr = reinterpret_cast<void *>(&_func) \
27+
}
28+
29+
#endif //ATS_JNI_MACROS_H

android-tree-sitter/src/main/java/com/itsaky/androidide/treesitter/TSLanguage.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package com.itsaky.androidide.treesitter;
1919

2020
import android.content.Context;
21+
import com.itsaky.androidide.treesitter.annotations.GenerateNativeHeaders;
2122
import com.itsaky.androidide.treesitter.util.TSObjectFactoryProvider;
2223
import java.nio.charset.StandardCharsets;
2324
import java.util.concurrent.atomic.AtomicLong;
@@ -249,6 +250,7 @@ private static void validateLangName(String lang) {
249250
}
250251
}
251252

253+
@GenerateNativeHeaders(fileName = "language")
252254
private static class Native {
253255

254256
private static native int symCount(long ptr);

android-tree-sitter/src/main/java/com/itsaky/androidide/treesitter/TreeSitter.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,64 @@
1717

1818
package com.itsaky.androidide.treesitter;
1919

20+
import com.itsaky.androidide.treesitter.annotations.GenerateNativeHeaders;
21+
2022
/**
2123
* @author Akash Yadav
2224
*/
2325
public class TreeSitter {
2426

27+
private static int sLangVer = -1, sMinCompatLangVer = -1;
28+
2529
/**
2630
* Loads the <code>android-tree-sitter</code> native library using
2731
* {@link System#loadLibrary(String)}.
2832
*/
2933
public static void loadLibrary() {
3034
System.loadLibrary("android-tree-sitter");
35+
Native.registerNatives();
3136
}
3237

38+
3339
/**
3440
* The latest ABI version that is supported by the current version of the library. When Languages
3541
* are generated by the Tree-sitter CLI, they are assigned an ABI version number that corresponds
3642
* to the current CLI version. The Tree-sitter library is generally backwards-compatible with
3743
* languages generated using older CLI versions, but is not forwards-compatible.
3844
*/
39-
public static native int getLanguageVersion();
45+
public static int getLanguageVersion() {
46+
if (sLangVer == -1) {
47+
sLangVer = Native.getLanguageVersion();
48+
}
49+
50+
return sLangVer;
51+
}
4052

4153
/**
4254
* The earliest ABI version that is supported by the current version of the library.
4355
*/
44-
public static native int getMinimumCompatibleLanguageVersion();
56+
public static int getMinimumCompatibleLanguageVersion() {
57+
if (sMinCompatLangVer == -1) {
58+
sMinCompatLangVer = Native.getMinimumCompatibleLanguageVersion();
59+
}
60+
61+
return sMinCompatLangVer;
62+
}
63+
64+
public static void registerNatives() {
65+
Native.registerNatives();
66+
}
67+
68+
@GenerateNativeHeaders(fileName = "meta")
69+
private static final class Native {
70+
71+
static native int getLanguageVersion();
72+
73+
/**
74+
* The earliest ABI version that is supported by the current version of the library.
75+
*/
76+
static native int getMinimumCompatibleLanguageVersion();
77+
78+
private static native void registerNatives();
79+
}
4580
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* This file is part of android-tree-sitter.
3+
*
4+
* android-tree-sitter library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* android-tree-sitter library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with android-tree-sitter. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.itsaky.androidide.treesitter.ap;
19+
20+
import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.AGGREGATING;
21+
22+
import com.google.auto.service.AutoService;
23+
import com.itsaky.androidide.treesitter.annotations.GenerateNativeHeaders;
24+
import com.itsaky.androidide.treesitter.annotations.Synchronized;
25+
import java.io.File;
26+
import java.io.FileOutputStream;
27+
import java.io.IOException;
28+
import java.nio.charset.StandardCharsets;
29+
import java.util.HashMap;
30+
import java.util.Objects;
31+
import java.util.Set;
32+
import javax.annotation.processing.AbstractProcessor;
33+
import javax.annotation.processing.ProcessingEnvironment;
34+
import javax.annotation.processing.Processor;
35+
import javax.annotation.processing.RoundEnvironment;
36+
import javax.annotation.processing.SupportedAnnotationTypes;
37+
import javax.annotation.processing.SupportedOptions;
38+
import javax.annotation.processing.SupportedSourceVersion;
39+
import javax.lang.model.SourceVersion;
40+
import javax.lang.model.element.Element;
41+
import javax.lang.model.element.ElementKind;
42+
import javax.lang.model.element.TypeElement;
43+
import javax.lang.model.util.Elements;
44+
import javax.lang.model.util.Types;
45+
import javax.tools.Diagnostic.Kind;
46+
import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
47+
48+
/**
49+
* Annotation processor for {@link GenerateNativeHeaders}.
50+
*
51+
* @author Akash Yadav
52+
*/
53+
@SupportedAnnotationTypes(value = {
54+
"com.itsaky.androidide.treesitter.annotations.GenerateNativeHeaders"})
55+
@SupportedOptions(value = {})
56+
@SupportedSourceVersion(SourceVersion.RELEASE_11)
57+
@IncrementalAnnotationProcessor(AGGREGATING)
58+
@AutoService(Processor.class)
59+
@SuppressWarnings("unused")
60+
public class GenerateNativeHeadersAnnotationProcessor extends AbstractProcessor {
61+
62+
private Types types;
63+
private Elements elements;
64+
65+
private File outputDirectory;
66+
67+
@Override
68+
public Set<String> getSupportedAnnotationTypes() {
69+
return Set.of(GenerateNativeHeaders.class.getName());
70+
}
71+
72+
@Override
73+
public synchronized void init(ProcessingEnvironment processingEnv) {
74+
this.types = processingEnv.getTypeUtils();
75+
this.elements = processingEnv.getElementUtils();
76+
this.outputDirectory = new File(
77+
Objects.requireNonNull(processingEnv.getOptions().get("nativeHeaders.outDir")));
78+
79+
super.init(processingEnv);
80+
}
81+
82+
@Override
83+
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
84+
final var messager = processingEnv.getMessager();
85+
final var elements = roundEnvironment.getElementsAnnotatedWith(GenerateNativeHeaders.class);
86+
for (Element element : elements) {
87+
if (element.getKind() != ElementKind.CLASS) {
88+
messager.printMessage(Kind.ERROR,
89+
Synchronized.class.getSimpleName() + " can only be applied to classes");
90+
continue;
91+
}
92+
93+
final var type = ((TypeElement) element);
94+
final var annotation = Objects.requireNonNull(
95+
element.getAnnotation(GenerateNativeHeaders.class));
96+
final var fileName = annotation.fileName();
97+
98+
final var writer = new JNIWriter(this.types, this.elements);
99+
final var result = writer.generate(type);
100+
101+
if (outputDirectory.exists()) {
102+
outputDirectory.delete();
103+
}
104+
105+
outputDirectory.mkdirs();
106+
107+
final var methodHeaders = new File(outputDirectory, "ts_" + fileName + ".h");
108+
if (methodHeaders.exists()) {
109+
methodHeaders.delete();
110+
}
111+
112+
final var methodSignatures = new File(outputDirectory, "ts_" + fileName + "_sigs.h");
113+
if (methodSignatures.exists()) {
114+
methodSignatures.delete();
115+
}
116+
117+
writeFileContents(result.first, methodHeaders);
118+
writeFileContents(result.second, methodSignatures);
119+
}
120+
return false;
121+
}
122+
123+
private static void writeFileContents(String methodHeaderContents, File methodHeaders) {
124+
try (final var out = new FileOutputStream(methodHeaders)) {
125+
out.write(methodHeaderContents.getBytes(StandardCharsets.UTF_8));
126+
} catch (IOException e) {
127+
throw new RuntimeException(e);
128+
}
129+
}
130+
}

0 commit comments

Comments
 (0)