From 9dfd347ef242f1195ef1d633a96b8f7f43f9036c Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Thu, 20 Sep 2018 12:57:57 +0300 Subject: [PATCH] Add workaround for unexpected libclang behaviour `clang_tokenize(tu, clang_getCursorExtent(cursor), ...)` doesn't seem to work properly when cursor points to expanded macro: the result includes too many tokens --- .../kotlin/native/interop/indexer/Indexer.kt | 67 +++++++++++-------- backend.native/tests/build.gradle | 10 +++ .../tests/interop/basics/cunsupported.def | 27 ++++++++ .../tests/interop/basics/unsupported.kt | 12 ++++ 4 files changed, 88 insertions(+), 28 deletions(-) create mode 100644 backend.native/tests/interop/basics/cunsupported.def create mode 100644 backend.native/tests/interop/basics/unsupported.kt diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt index 2155b14ef59..d4f4299dd4c 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt @@ -653,44 +653,55 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() { private val TARGET_ATTRIBUTE = "__target__" - internal fun tokenizeExtent(cursor: CValue): List { - val translationUnit = clang_Cursor_getTranslationUnit(cursor)!! - val cursorExtent = clang_getCursorExtent(cursor) - memScoped { - val tokensVar = alloc>() - val numTokensVar = alloc() - clang_tokenize(translationUnit, cursorExtent, tokensVar.ptr, numTokensVar.ptr) - val numTokens = numTokensVar.value - val tokens = tokensVar.value - if (tokens == null) return emptyList() - try { - return (0 until numTokens).map { - clang_getTokenSpelling(translationUnit, tokens[it].readValue()).convertAndDispose() - } - } finally { - clang_disposeTokens(translationUnit, tokens, numTokens) - } - } - } - private fun isSuitableFunction(cursor: CValue): Boolean { if (!isAvailable(cursor)) return false // If function is specific for certain target, ignore that, as we may be // unable to generate machine code for bridge from the bitcode. + return !functionHasTargetAttribute(cursor) + } + + private fun functionHasTargetAttribute(cursor: CValue): Boolean { // TODO: this must be implemented with hasAttribute(), but hasAttribute() // works for Mac hosts only so far. - var suitable = true + + var result = false visitChildren(cursor) { child, _ -> - if (clang_isAttribute(child.kind) != 0) { - suitable = !tokenizeExtent(child).any { it == TARGET_ATTRIBUTE } - } - if (suitable) - CXChildVisitResult.CXChildVisit_Continue - else + if (isTargetAttribute(child)) { + result = true CXChildVisitResult.CXChildVisit_Break + } else { + CXChildVisitResult.CXChildVisit_Continue + } + } + return result + } + + private fun isTargetAttribute(cursor: CValue): Boolean = clang_isAttribute(cursor.kind) != 0 && + getExtentFirstToken(cursor) == TARGET_ATTRIBUTE + + private fun getExtentFirstToken(cursor: CValue) = + getToken(clang_Cursor_getTranslationUnit(cursor)!!, clang_getRangeStart(clang_getCursorExtent(cursor))) + + // TODO: implement with clang_getToken after updating libclang. + private fun getToken(translationUnit: CXTranslationUnit, location: CValue): String? = memScoped { + val range = clang_getRange(location, location) // Seems to work. + + val tokensVar = alloc>() + val numTokensVar = alloc() + clang_tokenize(translationUnit, range, tokensVar.ptr, numTokensVar.ptr) + val numTokens = numTokensVar.value + val tokens = tokensVar.value ?: return null + + try { + when (numTokens) { + 0 -> null + 1 -> clang_getTokenSpelling(translationUnit, tokens[0].readValue()).convertAndDispose() + else -> error("Unexpected number of tokens: $numTokens") + } + } finally { + clang_disposeTokens(translationUnit, tokens, numTokens) } - return suitable } fun indexDeclaration(info: CXIdxDeclInfo): Unit { diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 2f28a25912f..3844f26fa61 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -2733,6 +2733,10 @@ kotlinNativeInterop { defFile 'interop/basics/cmacros.def' } + cunsupported { + defFile 'interop/basics/cunsupported.def' + } + if (isMac()) { objcSmoke { defFile 'interop/objc/objcSmoke.def' @@ -2820,6 +2824,12 @@ task interop_macros(type: RunInteropKonanTest) { interop = 'cmacros' } +task interop_unsupported(type: RunInteropKonanTest) { + disabled = (project.testTarget == 'wasm32') // No interop for wasm yet. + source = "interop/basics/unsupported.kt" + interop = 'cunsupported' +} + task interop_echo_server(type: RunInteropKonanTest) { disabled = (project.testTarget == 'wasm32') // No interop for wasm yet. if (!isMac()) { diff --git a/backend.native/tests/interop/basics/cunsupported.def b/backend.native/tests/interop/basics/cunsupported.def new file mode 100644 index 00000000000..e4c19142291 --- /dev/null +++ b/backend.native/tests/interop/basics/cunsupported.def @@ -0,0 +1,27 @@ +compilerOpts = -mno-xsave + +--- + +static void noAttr() {} +__attribute__((always_inline)) noTargetAttr() {} + +__attribute__((always_inline, __target__("xsave"))) void plainAttrs1() {} +__attribute__((__target__("xsave"), always_inline)) void plainAttrs2() {} + +#define TARGET __target__ + +__attribute__((always_inline, TARGET("xsave"))) void macroAttr1() {} +__attribute__((TARGET("xsave"), always_inline)) void macroAttr2() {} + +#define TARGET_ATTR __target__("xsave") + +__attribute__((TARGET_ATTR, always_inline)) void macroAttr3() {} +__attribute__((always_inline, TARGET_ATTR)) void macroAttr4() {} + +#define ALL_ATTRS1 __attribute__((always_inline, __target__("xsave"))) + +ALL_ATTRS1 void macroAttr5() {} + +#define ALL_ATTRS2 __attribute__((always_inline, __target__("xsave"))) + +ALL_ATTRS2 void macroAttr6() {} diff --git a/backend.native/tests/interop/basics/unsupported.kt b/backend.native/tests/interop/basics/unsupported.kt new file mode 100644 index 00000000000..08bfe60a730 --- /dev/null +++ b/backend.native/tests/interop/basics/unsupported.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +import kotlin.test.* +import cunsupported.* + +fun main(args: Array) { + noAttr() + noTargetAttr() +}