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
This commit is contained in:
committed by
SvyatoslavScherbina
parent
30fd4060d3
commit
9dfd347ef2
+39
-28
@@ -653,44 +653,55 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
|
||||
private val TARGET_ATTRIBUTE = "__target__"
|
||||
|
||||
internal fun tokenizeExtent(cursor: CValue<CXCursor>): List<String> {
|
||||
val translationUnit = clang_Cursor_getTranslationUnit(cursor)!!
|
||||
val cursorExtent = clang_getCursorExtent(cursor)
|
||||
memScoped {
|
||||
val tokensVar = alloc<CPointerVar<CXToken>>()
|
||||
val numTokensVar = alloc<IntVar>()
|
||||
clang_tokenize(translationUnit, cursorExtent, tokensVar.ptr, numTokensVar.ptr)
|
||||
val numTokens = numTokensVar.value
|
||||
val tokens = tokensVar.value
|
||||
if (tokens == null) return emptyList<String>()
|
||||
try {
|
||||
return (0 until numTokens).map {
|
||||
clang_getTokenSpelling(translationUnit, tokens[it].readValue()).convertAndDispose()
|
||||
}
|
||||
} finally {
|
||||
clang_disposeTokens(translationUnit, tokens, numTokens)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSuitableFunction(cursor: CValue<CXCursor>): 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<CXCursor>): 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<CXCursor>): Boolean = clang_isAttribute(cursor.kind) != 0 &&
|
||||
getExtentFirstToken(cursor) == TARGET_ATTRIBUTE
|
||||
|
||||
private fun getExtentFirstToken(cursor: CValue<CXCursor>) =
|
||||
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<CXSourceLocation>): String? = memScoped {
|
||||
val range = clang_getRange(location, location) // Seems to work.
|
||||
|
||||
val tokensVar = alloc<CPointerVar<CXToken>>()
|
||||
val numTokensVar = alloc<IntVar>()
|
||||
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 {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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() {}
|
||||
@@ -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<String>) {
|
||||
noAttr()
|
||||
noTargetAttr()
|
||||
}
|
||||
Reference in New Issue
Block a user