Skip to content

Commit c99e105

Browse files
* Add new ClangMemoryMgmtExample in samples for LLVM (pull bytedeco#1522)
1 parent f71d036 commit c99e105

File tree

3 files changed

+246
-3
lines changed

3 files changed

+246
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11

2+
* Add new `ClangMemoryMgmtExample` in samples for LLVM ([pull #1522](https://github.com/bytedeco/javacpp-presets/pull/1522))
23
* Enable `opencv_python3` module for `macosx-arm64` as well ([pull #1517](https://github.com/bytedeco/javacpp-presets/pull/1517))
34
* Introduce `macosx-arm64` builds for CPython ([pull #1511](https://github.com/bytedeco/javacpp-presets/pull/1511)), NumPy ([pull #1515](https://github.com/bytedeco/javacpp-presets/pull/1515)), SciPy ([pull #1516](https://github.com/bytedeco/javacpp-presets/pull/1516))
45
* Update and fix the sample code of the presets for LLVM ([pull #1501](https://github.com/bytedeco/javacpp-presets/pull/1501))
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import org.bytedeco.javacpp.BytePointer;
2+
import org.bytedeco.javacpp.Pointer;
3+
import org.bytedeco.javacpp.PointerPointer;
4+
import org.bytedeco.javacpp.PointerScope;
5+
import org.bytedeco.llvm.clang.CXClientData;
6+
import org.bytedeco.llvm.clang.CXCursor;
7+
import org.bytedeco.llvm.clang.CXCursorVisitor;
8+
import org.bytedeco.llvm.clang.CXIndex;
9+
import org.bytedeco.llvm.clang.CXSourceRange;
10+
import org.bytedeco.llvm.clang.CXString;
11+
import org.bytedeco.llvm.clang.CXToken;
12+
import org.bytedeco.llvm.clang.CXTranslationUnit;
13+
import org.bytedeco.llvm.clang.CXUnsavedFile;
14+
import org.bytedeco.llvm.global.clang;
15+
16+
import java.net.URISyntaxException;
17+
import java.net.URL;
18+
import java.nio.file.Path;
19+
import java.nio.file.Paths;
20+
import java.util.List;
21+
import java.util.function.Consumer;
22+
import java.util.function.Supplier;
23+
import java.util.stream.IntStream;
24+
25+
import static java.lang.System.out;
26+
import static java.util.Arrays.asList;
27+
import static java.util.Objects.requireNonNull;
28+
import static org.bytedeco.llvm.global.clang.CXChildVisit_Recurse;
29+
import static org.bytedeco.llvm.global.clang.CXError_Success;
30+
import static org.bytedeco.llvm.global.clang.CXTranslationUnit_None;
31+
import static org.bytedeco.llvm.global.clang.clang_Cursor_getTranslationUnit;
32+
import static org.bytedeco.llvm.global.clang.clang_createIndex;
33+
import static org.bytedeco.llvm.global.clang.clang_disposeIndex;
34+
import static org.bytedeco.llvm.global.clang.clang_disposeTokens;
35+
import static org.bytedeco.llvm.global.clang.clang_disposeTranslationUnit;
36+
import static org.bytedeco.llvm.global.clang.clang_getCursorExtent;
37+
import static org.bytedeco.llvm.global.clang.clang_getCursorKind;
38+
import static org.bytedeco.llvm.global.clang.clang_getCursorKindSpelling;
39+
import static org.bytedeco.llvm.global.clang.clang_getTokenKind;
40+
import static org.bytedeco.llvm.global.clang.clang_getTokenSpelling;
41+
import static org.bytedeco.llvm.global.clang.clang_getTranslationUnitCursor;
42+
import static org.bytedeco.llvm.global.clang.clang_parseTranslationUnit2;
43+
import static org.bytedeco.llvm.global.clang.clang_tokenize;
44+
import static org.bytedeco.llvm.global.clang.clang_visitChildren;
45+
46+
/**
47+
* Demonstrates how to avoid memory leaks when traversing the AST and tokenizing
48+
* ranges.
49+
*
50+
* <p>
51+
* This includes:
52+
* <ul>
53+
* <li><em>Try-with-resources</em> for any {@link AutoCloseable} instances
54+
* (incl. {@link Pointer} descendants);</li>
55+
* <li>usage of {@link PointerScope} (see {@link #withPointerScope(Runnable)});</li>
56+
* <li>invocation of {@link clang#clang_disposeIndex(CXIndex)}, {@link
57+
* clang#clang_disposeTranslationUnit(CXTranslationUnit)}, and {@link
58+
* clang#clang_disposeTokens(CXTranslationUnit, CXToken, int)} where
59+
* appropriate.</li>
60+
* </ul>
61+
* </p>
62+
*
63+
* @see AutoCloseable
64+
* @see Pointer
65+
* @see PointerScope
66+
* @see #withPointerScope(Runnable)
67+
* @see clang#clang_disposeIndex(CXIndex)
68+
* @see clang#clang_disposeTranslationUnit(CXTranslationUnit)
69+
* @see clang#clang_disposeTokens(CXTranslationUnit, CXToken, int)
70+
* @author Andrey Shcheglov
71+
*/
72+
public final class ClangMemoryMgmtExample {
73+
private ClangMemoryMgmtExample() {
74+
assert false;
75+
}
76+
77+
public static void main(final String... args) throws URISyntaxException {
78+
final URL codeUrl = requireNonNull(ClangMemoryMgmtExample.class.getResource("/sample1.cc"));
79+
final Path absoluteFile = Paths.get(codeUrl.toURI()).toAbsolutePath().normalize();
80+
parse(absoluteFile, asList("-std=gnu++20", "-fparse-all-comments"));
81+
}
82+
83+
private static void parse(
84+
final Path absoluteFile,
85+
final List<String> commandLineArgs
86+
) {
87+
withPointerScope(() -> {
88+
withIndex(clang_createIndex(1, 0), index -> {
89+
withTranslationUnit(CXTranslationUnit::new, translationUnit -> {
90+
try (final BytePointer sourceFilename = new BytePointer(absoluteFile.toString())) {
91+
try (final PointerPointer<Pointer> commandLineArgsPtr = new PointerPointer<>(commandLineArgs.toArray(new String[0]))) {
92+
try (final CXUnsavedFile unsavedFiles = new CXUnsavedFile()) {
93+
final int unsavedFilesCount = 0;
94+
95+
final int errorCode = clang_parseTranslationUnit2(
96+
index,
97+
sourceFilename,
98+
commandLineArgsPtr,
99+
commandLineArgs.size(),
100+
unsavedFiles,
101+
unsavedFilesCount,
102+
CXTranslationUnit_None,
103+
translationUnit
104+
);
105+
106+
if (errorCode == CXError_Success) {
107+
try (final CXCursor rootCursor = clang_getTranslationUnitCursor(translationUnit)) {
108+
clang_visitChildren(rootCursor, new AstVisitor(), null);
109+
}
110+
} else {
111+
out.printf("Failed to parse %s; parser returned code %d%n", absoluteFile, errorCode);
112+
}
113+
}
114+
}
115+
}
116+
});
117+
});
118+
});
119+
}
120+
121+
private static void withPointerScope(final Runnable block) {
122+
try (final PointerScope ignored = new PointerScope()) {
123+
block.run();
124+
}
125+
}
126+
127+
private static void withIndex(
128+
final CXIndex index,
129+
final Consumer<CXIndex> block
130+
) {
131+
try (final CXIndex ignored = index) {
132+
try {
133+
block.accept(index);
134+
} finally {
135+
clang_disposeIndex(index);
136+
}
137+
}
138+
}
139+
140+
private static void withTranslationUnit(
141+
final Supplier<CXTranslationUnit> lazyTranslationUnit,
142+
final Consumer<CXTranslationUnit> block
143+
) {
144+
try (final CXTranslationUnit translationUnit = lazyTranslationUnit.get()) {
145+
try {
146+
block.accept(translationUnit);
147+
} finally {
148+
clang_disposeTranslationUnit(translationUnit);
149+
}
150+
}
151+
}
152+
153+
private static void forEachToken(
154+
final CXCursor cursor,
155+
final Consumer<? super CXToken> action
156+
) {
157+
try (final CXSourceRange extent = clang_getCursorExtent(cursor)) {
158+
try (final CXTranslationUnit translationUnit = clang_Cursor_getTranslationUnit(cursor)) {
159+
try (final CXToken tokens = new CXToken()) {
160+
final int[] tokenCountRef = new int[1];
161+
clang_tokenize(translationUnit, extent, tokens, tokenCountRef);
162+
final int tokenCount = tokenCountRef[0];
163+
try {
164+
IntStream.range(0, tokenCount)
165+
.mapToObj(tokens::position)
166+
.forEach(action);
167+
} finally {
168+
tokens.position(0L);
169+
clang_disposeTokens(translationUnit, tokens, tokenCount);
170+
}
171+
}
172+
}
173+
}
174+
}
175+
176+
private static final class AstVisitor extends CXCursorVisitor {
177+
@Override
178+
public int call(final CXCursor cursor, final CXCursor parent, final CXClientData clientData) {
179+
try (final CXCursor c = cursor; final CXCursor p = parent; final CXClientData d = clientData) {
180+
/*-
181+
* Entering a new `PointerScope` here is 100% necessary,
182+
* probably because the outer ("lower") stack frame is a
183+
* native one (i.e. `call()` is directly invoked by the
184+
* native code).
185+
*
186+
* Despite previously registered ("outer") pointer scopes
187+
* are still visible, having only a single scope per
188+
* translation unit (i.e., AST tree) rather than per cursor
189+
* eventually results in 100% usage of all CPU cores -- in
190+
* the native code.
191+
*/
192+
withPointerScope(() -> {
193+
try (final CXString spelling = clang_getCursorKindSpelling(clang_getCursorKind(cursor))) {
194+
out.println(spelling.getString());
195+
}
196+
197+
try (final CXTranslationUnit translationUnit = clang_Cursor_getTranslationUnit(cursor)) {
198+
forEachToken(cursor, token -> {
199+
final TokenKind kind = TokenKind.valueOf(clang_getTokenKind(token));
200+
try (final CXString spelling = clang_getTokenSpelling(translationUnit, token)) {
201+
out.printf("\t%s(\"%s\")%n", kind, spelling.getString());
202+
}
203+
});
204+
}
205+
206+
});
207+
}
208+
209+
return CXChildVisit_Recurse;
210+
}
211+
}
212+
213+
private enum TokenKind {
214+
Punctuation,
215+
216+
Keyword,
217+
218+
Identifier,
219+
220+
Literal,
221+
222+
Comment,
223+
;
224+
225+
private static TokenKind valueOf(final int ordinal) {
226+
return values()[ordinal];
227+
}
228+
}
229+
}

llvm/samples/clang/pom.xml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<version>1.5.11-SNAPSHOT</version>
66
<properties>
77
<exec.mainClass>ClangASTVisitorExample</exec.mainClass>
8-
<maven.compiler.source>1.7</maven.compiler.source>
9-
<maven.compiler.target>1.7</maven.compiler.target>
8+
<maven.compiler.source>8</maven.compiler.source>
9+
<maven.compiler.target>8</maven.compiler.target>
1010
</properties>
1111
<dependencies>
1212
<dependency>
@@ -16,6 +16,19 @@
1616
</dependency>
1717
</dependencies>
1818
<build>
19-
<sourceDirectory>.</sourceDirectory>
19+
<sourceDirectory>${project.basedir}</sourceDirectory>
20+
<resources>
21+
<resource>
22+
<directory>${project.basedir}</directory>
23+
<includes>
24+
<include>**/*.cc</include>
25+
</includes>
26+
<excludes>
27+
<exclude>**/*.java</exclude>
28+
<exclude>**/pom.xml</exclude>
29+
<exclude>target/**/*</exclude>
30+
</excludes>
31+
</resource>
32+
</resources>
2033
</build>
2134
</project>

0 commit comments

Comments
 (0)