From c99e10548dc345bdcffc6ce8bfa1a0b5d73a238a Mon Sep 17 00:00:00 2001 From: "Andrey S." Date: Sat, 3 Aug 2024 06:48:32 +0300 Subject: [PATCH] * Add new `ClangMemoryMgmtExample` in samples for LLVM (pull #1522) --- CHANGELOG.md | 1 + .../samples/clang/ClangMemoryMgmtExample.java | 229 ++++++++++++++++++ llvm/samples/clang/pom.xml | 19 +- 3 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 llvm/samples/clang/ClangMemoryMgmtExample.java diff --git a/CHANGELOG.md b/CHANGELOG.md index dcc1a603153..163dd929aea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ + * Add new `ClangMemoryMgmtExample` in samples for LLVM ([pull #1522](https://github.com/bytedeco/javacpp-presets/pull/1522)) * Enable `opencv_python3` module for `macosx-arm64` as well ([pull #1517](https://github.com/bytedeco/javacpp-presets/pull/1517)) * 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)) * Update and fix the sample code of the presets for LLVM ([pull #1501](https://github.com/bytedeco/javacpp-presets/pull/1501)) diff --git a/llvm/samples/clang/ClangMemoryMgmtExample.java b/llvm/samples/clang/ClangMemoryMgmtExample.java new file mode 100644 index 00000000000..ba970f03ddd --- /dev/null +++ b/llvm/samples/clang/ClangMemoryMgmtExample.java @@ -0,0 +1,229 @@ +import org.bytedeco.javacpp.BytePointer; +import org.bytedeco.javacpp.Pointer; +import org.bytedeco.javacpp.PointerPointer; +import org.bytedeco.javacpp.PointerScope; +import org.bytedeco.llvm.clang.CXClientData; +import org.bytedeco.llvm.clang.CXCursor; +import org.bytedeco.llvm.clang.CXCursorVisitor; +import org.bytedeco.llvm.clang.CXIndex; +import org.bytedeco.llvm.clang.CXSourceRange; +import org.bytedeco.llvm.clang.CXString; +import org.bytedeco.llvm.clang.CXToken; +import org.bytedeco.llvm.clang.CXTranslationUnit; +import org.bytedeco.llvm.clang.CXUnsavedFile; +import org.bytedeco.llvm.global.clang; + +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.IntStream; + +import static java.lang.System.out; +import static java.util.Arrays.asList; +import static java.util.Objects.requireNonNull; +import static org.bytedeco.llvm.global.clang.CXChildVisit_Recurse; +import static org.bytedeco.llvm.global.clang.CXError_Success; +import static org.bytedeco.llvm.global.clang.CXTranslationUnit_None; +import static org.bytedeco.llvm.global.clang.clang_Cursor_getTranslationUnit; +import static org.bytedeco.llvm.global.clang.clang_createIndex; +import static org.bytedeco.llvm.global.clang.clang_disposeIndex; +import static org.bytedeco.llvm.global.clang.clang_disposeTokens; +import static org.bytedeco.llvm.global.clang.clang_disposeTranslationUnit; +import static org.bytedeco.llvm.global.clang.clang_getCursorExtent; +import static org.bytedeco.llvm.global.clang.clang_getCursorKind; +import static org.bytedeco.llvm.global.clang.clang_getCursorKindSpelling; +import static org.bytedeco.llvm.global.clang.clang_getTokenKind; +import static org.bytedeco.llvm.global.clang.clang_getTokenSpelling; +import static org.bytedeco.llvm.global.clang.clang_getTranslationUnitCursor; +import static org.bytedeco.llvm.global.clang.clang_parseTranslationUnit2; +import static org.bytedeco.llvm.global.clang.clang_tokenize; +import static org.bytedeco.llvm.global.clang.clang_visitChildren; + +/** + * Demonstrates how to avoid memory leaks when traversing the AST and tokenizing + * ranges. + * + *

+ * This includes: + *

+ *

+ * + * @see AutoCloseable + * @see Pointer + * @see PointerScope + * @see #withPointerScope(Runnable) + * @see clang#clang_disposeIndex(CXIndex) + * @see clang#clang_disposeTranslationUnit(CXTranslationUnit) + * @see clang#clang_disposeTokens(CXTranslationUnit, CXToken, int) + * @author Andrey Shcheglov + */ +public final class ClangMemoryMgmtExample { + private ClangMemoryMgmtExample() { + assert false; + } + + public static void main(final String... args) throws URISyntaxException { + final URL codeUrl = requireNonNull(ClangMemoryMgmtExample.class.getResource("/sample1.cc")); + final Path absoluteFile = Paths.get(codeUrl.toURI()).toAbsolutePath().normalize(); + parse(absoluteFile, asList("-std=gnu++20", "-fparse-all-comments")); + } + + private static void parse( + final Path absoluteFile, + final List commandLineArgs + ) { + withPointerScope(() -> { + withIndex(clang_createIndex(1, 0), index -> { + withTranslationUnit(CXTranslationUnit::new, translationUnit -> { + try (final BytePointer sourceFilename = new BytePointer(absoluteFile.toString())) { + try (final PointerPointer commandLineArgsPtr = new PointerPointer<>(commandLineArgs.toArray(new String[0]))) { + try (final CXUnsavedFile unsavedFiles = new CXUnsavedFile()) { + final int unsavedFilesCount = 0; + + final int errorCode = clang_parseTranslationUnit2( + index, + sourceFilename, + commandLineArgsPtr, + commandLineArgs.size(), + unsavedFiles, + unsavedFilesCount, + CXTranslationUnit_None, + translationUnit + ); + + if (errorCode == CXError_Success) { + try (final CXCursor rootCursor = clang_getTranslationUnitCursor(translationUnit)) { + clang_visitChildren(rootCursor, new AstVisitor(), null); + } + } else { + out.printf("Failed to parse %s; parser returned code %d%n", absoluteFile, errorCode); + } + } + } + } + }); + }); + }); + } + + private static void withPointerScope(final Runnable block) { + try (final PointerScope ignored = new PointerScope()) { + block.run(); + } + } + + private static void withIndex( + final CXIndex index, + final Consumer block + ) { + try (final CXIndex ignored = index) { + try { + block.accept(index); + } finally { + clang_disposeIndex(index); + } + } + } + + private static void withTranslationUnit( + final Supplier lazyTranslationUnit, + final Consumer block + ) { + try (final CXTranslationUnit translationUnit = lazyTranslationUnit.get()) { + try { + block.accept(translationUnit); + } finally { + clang_disposeTranslationUnit(translationUnit); + } + } + } + + private static void forEachToken( + final CXCursor cursor, + final Consumer action + ) { + try (final CXSourceRange extent = clang_getCursorExtent(cursor)) { + try (final CXTranslationUnit translationUnit = clang_Cursor_getTranslationUnit(cursor)) { + try (final CXToken tokens = new CXToken()) { + final int[] tokenCountRef = new int[1]; + clang_tokenize(translationUnit, extent, tokens, tokenCountRef); + final int tokenCount = tokenCountRef[0]; + try { + IntStream.range(0, tokenCount) + .mapToObj(tokens::position) + .forEach(action); + } finally { + tokens.position(0L); + clang_disposeTokens(translationUnit, tokens, tokenCount); + } + } + } + } + } + + private static final class AstVisitor extends CXCursorVisitor { + @Override + public int call(final CXCursor cursor, final CXCursor parent, final CXClientData clientData) { + try (final CXCursor c = cursor; final CXCursor p = parent; final CXClientData d = clientData) { + /*- + * Entering a new `PointerScope` here is 100% necessary, + * probably because the outer ("lower") stack frame is a + * native one (i.e. `call()` is directly invoked by the + * native code). + * + * Despite previously registered ("outer") pointer scopes + * are still visible, having only a single scope per + * translation unit (i.e., AST tree) rather than per cursor + * eventually results in 100% usage of all CPU cores -- in + * the native code. + */ + withPointerScope(() -> { + try (final CXString spelling = clang_getCursorKindSpelling(clang_getCursorKind(cursor))) { + out.println(spelling.getString()); + } + + try (final CXTranslationUnit translationUnit = clang_Cursor_getTranslationUnit(cursor)) { + forEachToken(cursor, token -> { + final TokenKind kind = TokenKind.valueOf(clang_getTokenKind(token)); + try (final CXString spelling = clang_getTokenSpelling(translationUnit, token)) { + out.printf("\t%s(\"%s\")%n", kind, spelling.getString()); + } + }); + } + + }); + } + + return CXChildVisit_Recurse; + } + } + + private enum TokenKind { + Punctuation, + + Keyword, + + Identifier, + + Literal, + + Comment, + ; + + private static TokenKind valueOf(final int ordinal) { + return values()[ordinal]; + } + } +} diff --git a/llvm/samples/clang/pom.xml b/llvm/samples/clang/pom.xml index ca83015f807..f19a6c60a40 100644 --- a/llvm/samples/clang/pom.xml +++ b/llvm/samples/clang/pom.xml @@ -5,8 +5,8 @@ 1.5.11-SNAPSHOT ClangASTVisitorExample - 1.7 - 1.7 + 8 + 8 @@ -16,6 +16,19 @@ - . + ${project.basedir} + + + ${project.basedir} + + **/*.cc + + + **/*.java + **/pom.xml + target/**/* + + +