diff --git a/Interop/Indexer/build.gradle b/Interop/Indexer/build.gradle index a0217d8a497..b064fe4bbd1 100644 --- a/Interop/Indexer/build.gradle +++ b/Interop/Indexer/build.gradle @@ -23,6 +23,45 @@ apply plugin: org.jetbrains.kotlin.NativeInteropPlugin apply plugin: 'c' +final Project libclangextProject = project(":libclangext") +final String libclangextTask = libclangextProject.path + ":build" +File libclangextDir = new File(libclangextProject.buildDir, "libs/clangext/static") +final boolean libclangextIsEnabled = libclangextProject.isEnabled + +final String libclang +if (isWindows()) { + libclang = "bin/libclang.dll" +} else { + libclang = "lib/${System.mapLibraryName("clang")}" +} + +List cflags = [ + "-I$llvmDir/include", + "-I${project(":libclangext").projectDir.absolutePath + "/src/main/include"}" +]*.toString() + +List ldflags = ["$llvmDir/$libclang", "-L$libclangextDir.absolutePath", "-lclangext"]*.toString() + +if (libclangextIsEnabled) { + assert(isMac()) + ldflags.addAll(['-Wl,--no-demangle', '-Wl,-search_paths_first', '-Wl,-headerpad_max_install_names']) + + List llvmLibs = [ + "clangAST", "clangASTMatchers", "clangAnalysis", "clangBasic", "clangDriver", "clangEdit", + "clangFrontend", "clangFrontendTool", "clangLex", "clangParse", "clangSema", "clangEdit", + "clangRewrite", "clangRewriteFrontend", "clangStaticAnalyzerFrontend", + "clangStaticAnalyzerCheckers", "clangStaticAnalyzerCore", "clangSerialization", + "clangToolingCore", + "clangTooling", "clangFormat", "LLVMTarget", "LLVMMC", "LLVMLinker", "LLVMTransformUtils", + "LLVMBitWriter", "LLVMBitReader", "LLVMAnalysis", "LLVMProfileData", "LLVMCore", + "LLVMSupport" + ].collect { "$llvmDir/lib/lib${it}.a".toString() } + + ldflags.addAll(llvmLibs) + ldflags.addAll(['-lpthread', '-lz', '-lm', '-lcurses']) +} + + model { components { clangstubs(NativeLibrarySpec) { @@ -32,17 +71,11 @@ model { binaries.all { cCompiler.args hostCompilerArgsForJni - cCompiler.args "-I$llvmDir/include" + cCompiler.args.addAll(cflags) } binaries.withType(SharedLibraryBinarySpec) { - final String libclang - if (isWindows()) { - libclang = "bin/libclang.dll" - } else { - libclang = "lib/${System.mapLibraryName("clang")}" - } - linker.args "$llvmDir/$libclang" + linker.args.addAll(ldflags) } } } @@ -74,12 +107,19 @@ classes.dependsOn nativelibs kotlinNativeInterop { clang { defFile 'clang.def' - compilerOpts "-I$llvmDir/include" - linkerOpts "-L$llvmDir/lib" + compilerOpts cflags + linkerOpts ldflags genTask.args '-keepcstubs', 'true' + genTask.dependsOn libclangextTask + genTask.inputs.dir libclangextDir } } +tasks.matching { it.name == 'linkClangstubsSharedLibrary' }.all { + it.dependsOn libclangextTask + it.inputs.dir libclangextDir +} + task updatePrebuilt { dependsOn genClangInteropStubs diff --git a/Interop/Indexer/clang.def b/Interop/Indexer/clang.def index 69cd445e40b..3738a9f8303 100644 --- a/Interop/Indexer/clang.def +++ b/Interop/Indexer/clang.def @@ -1,4 +1,4 @@ -headers = clang-c/Index.h +headers = clang-c/Index.h clang-c/ext.h headerFilter = clang-c/** @@ -7,10 +7,9 @@ compiler = clang compilerOpts = -std=c99 -fPIC linkerOpts.linux = -Wl,-z,noexecstack -linker = clang +linker = clang++ -linkerOpts = -fPIC \ - -lclang +linkerOpts = -fPIC strictEnums = CXErrorCode CXCursorKind CXTypeKind CXDiagnosticSeverity CXLoadDiag_Error CXSaveError \ CXTUResourceUsageKind CXLinkageKind CXVisibilityKind CXLanguageKind CXCallingConv CXChildVisitResult \ diff --git a/Interop/Indexer/prebuilt/nativeInteropStubs/c/clangstubs.c b/Interop/Indexer/prebuilt/nativeInteropStubs/c/clangstubs.c index 568bc7fb26f..0dd71920819 100644 --- a/Interop/Indexer/prebuilt/nativeInteropStubs/c/clangstubs.c +++ b/Interop/Indexer/prebuilt/nativeInteropStubs/c/clangstubs.c @@ -1,6 +1,7 @@ #include #include #include +#include JNIEXPORT jlong JNICALL Java_clang_clang_kni_1clang_1getCString (JNIEnv *jniEnv, jclass jclss, jlong string) { return (jlong) (clang_getCString(*(CXString*)string)); @@ -1239,3 +1240,39 @@ JNIEXPORT jint JNICALL Java_clang_clang_kni_1clang_1Type_1visitFields (JNIEnv *j return (jint) (clang_Type_visitFields(*(CXType*)T, (CXFieldVisitor)visitor, (CXClientData)client_data)); } +JNIEXPORT jlong JNICALL Java_clang_clang_kni_1clang_1Cursor_1getAttributeSpelling (JNIEnv *jniEnv, jclass jclss, jlong cursor) { + return (jlong) (clang_Cursor_getAttributeSpelling(*(CXCursor*)cursor)); +} + +JNIEXPORT jlong JNICALL Java_clang_clang_kni_1clang_1getDeclTypeAttributes (JNIEnv *jniEnv, jclass jclss, jlong cursor, jlong retValPlacement) { + *(CXTypeAttributes*)retValPlacement = clang_getDeclTypeAttributes(*(CXCursor*)cursor); + return (jlong) retValPlacement; +} + +JNIEXPORT jlong JNICALL Java_clang_clang_kni_1clang_1getResultTypeAttributes (JNIEnv *jniEnv, jclass jclss, jlong typeAttributes, jlong retValPlacement) { + *(CXTypeAttributes*)retValPlacement = clang_getResultTypeAttributes(*(CXTypeAttributes*)typeAttributes); + return (jlong) retValPlacement; +} + +JNIEXPORT jlong JNICALL Java_clang_clang_kni_1clang_1getCursorResultTypeAttributes (JNIEnv *jniEnv, jclass jclss, jlong cursor, jlong retValPlacement) { + *(CXTypeAttributes*)retValPlacement = clang_getCursorResultTypeAttributes(*(CXCursor*)cursor); + return (jlong) retValPlacement; +} + +JNIEXPORT jint JNICALL Java_clang_clang_kni_1clang_1Type_1getNullabilityKind (JNIEnv *jniEnv, jclass jclss, jlong type, jlong attributes) { + return (jint) (clang_Type_getNullabilityKind(*(CXType*)type, *(CXTypeAttributes*)attributes)); +} + +JNIEXPORT jint JNICALL Java_clang_clang_kni_1clang_1Type_1getNumProtocols (JNIEnv *jniEnv, jclass jclss, jlong type) { + return (jint) (clang_Type_getNumProtocols(*(CXType*)type)); +} + +JNIEXPORT jlong JNICALL Java_clang_clang_kni_1clang_1Type_1getProtocol (JNIEnv *jniEnv, jclass jclss, jlong type, jint index, jlong retValPlacement) { + *(CXCursor*)retValPlacement = clang_Type_getProtocol(*(CXType*)type, (unsigned int)index); + return (jlong) retValPlacement; +} + +JNIEXPORT jint JNICALL Java_clang_clang_kni_1clang_1Cursor_1isObjCInitMethod (JNIEnv *jniEnv, jclass jclss, jlong cursor) { + return (jint) (clang_Cursor_isObjCInitMethod(*(CXCursor*)cursor)); +} + diff --git a/Interop/Indexer/prebuilt/nativeInteropStubs/kotlin/clang/clang.kt b/Interop/Indexer/prebuilt/nativeInteropStubs/kotlin/clang/clang.kt index 73de13df2cf..ec3120cd1a5 100644 --- a/Interop/Indexer/prebuilt/nativeInteropStubs/kotlin/clang/clang.kt +++ b/Interop/Indexer/prebuilt/nativeInteropStubs/kotlin/clang/clang.kt @@ -2962,6 +2962,88 @@ fun clang_Type_visitFields(T: CValue, visitor: CXFieldVisitor?, client_d private external fun kni_clang_Type_visitFields(T: NativePtr, visitor: NativePtr, client_data: NativePtr): Int +fun clang_Cursor_getAttributeSpelling(cursor: CValue): CPointer? { + return memScoped { + val _cursor = cursor.getPointer(memScope).rawValue + val res = kni_clang_Cursor_getAttributeSpelling(_cursor) + interpretCPointer(res) + } +} + +private external fun kni_clang_Cursor_getAttributeSpelling(cursor: NativePtr): NativePtr + +fun clang_getDeclTypeAttributes(cursor: CValue): CValue { + return memScoped { + val _cursor = cursor.getPointer(memScope).rawValue + val res = kni_clang_getDeclTypeAttributes(_cursor, alloc().rawPtr) + interpretPointed(res).readValue() + } +} + +private external fun kni_clang_getDeclTypeAttributes(cursor: NativePtr, retValPlacement: NativePtr): NativePtr + +fun clang_getResultTypeAttributes(typeAttributes: CValue): CValue { + return memScoped { + val _typeAttributes = typeAttributes.getPointer(memScope).rawValue + val res = kni_clang_getResultTypeAttributes(_typeAttributes, alloc().rawPtr) + interpretPointed(res).readValue() + } +} + +private external fun kni_clang_getResultTypeAttributes(typeAttributes: NativePtr, retValPlacement: NativePtr): NativePtr + +fun clang_getCursorResultTypeAttributes(cursor: CValue): CValue { + return memScoped { + val _cursor = cursor.getPointer(memScope).rawValue + val res = kni_clang_getCursorResultTypeAttributes(_cursor, alloc().rawPtr) + interpretPointed(res).readValue() + } +} + +private external fun kni_clang_getCursorResultTypeAttributes(cursor: NativePtr, retValPlacement: NativePtr): NativePtr + +fun clang_Type_getNullabilityKind(type: CValue, attributes: CValue): CXNullabilityKind { + return memScoped { + val _type = type.getPointer(memScope).rawValue + val _attributes = attributes.getPointer(memScope).rawValue + val res = kni_clang_Type_getNullabilityKind(_type, _attributes) + CXNullabilityKind.byValue(res) + } +} + +private external fun kni_clang_Type_getNullabilityKind(type: NativePtr, attributes: NativePtr): Int + +fun clang_Type_getNumProtocols(type: CValue): Int { + return memScoped { + val _type = type.getPointer(memScope).rawValue + val res = kni_clang_Type_getNumProtocols(_type) + res + } +} + +private external fun kni_clang_Type_getNumProtocols(type: NativePtr): Int + +fun clang_Type_getProtocol(type: CValue, index: Int): CValue { + return memScoped { + val _type = type.getPointer(memScope).rawValue + val _index = index + val res = kni_clang_Type_getProtocol(_type, _index, alloc().rawPtr) + interpretPointed(res).readValue() + } +} + +private external fun kni_clang_Type_getProtocol(type: NativePtr, index: Int, retValPlacement: NativePtr): NativePtr + +fun clang_Cursor_isObjCInitMethod(cursor: CValue): Int { + return memScoped { + val _cursor = cursor.getPointer(memScope).rawValue + val res = kni_clang_Cursor_isObjCInitMethod(_cursor) + res + } +} + +private external fun kni_clang_Cursor_isObjCInitMethod(cursor: NativePtr): Int + val CINDEX_VERSION_MAJOR: Int = 0 val CINDEX_VERSION_MINOR: Int = 35 @@ -3682,6 +3764,17 @@ class IndexerCallbacks(override val rawPtr: NativePtr) : CStructVar() { } +@CNaturalStruct("typeOpaquePtr") +class CXTypeAttributes(override val rawPtr: NativePtr) : CStructVar() { + + companion object : Type(8, 8) + + var typeOpaquePtr: COpaquePointer? + get() = memberAt(0).value + set(value) { memberAt(0).value = value } + +} + enum class CXErrorCode(override val value: Int) : CEnum { CXError_Success(0), CXError_Failure(1), @@ -4630,6 +4723,24 @@ val CXIndexOpt_IndexImplicitTemplateInstantiations: CXIndexOptFlags = 4 val CXIndexOpt_SuppressWarnings: CXIndexOptFlags = 8 val CXIndexOpt_SkipParsedBodiesInSession: CXIndexOptFlags = 16 +enum class CXNullabilityKind(override val value: Int) : CEnum { + CXNullabilityKind_Nullable(0), + CXNullabilityKind_NonNull(1), + CXNullabilityKind_Unspecified(2), + ; + + companion object { + fun byValue(value: Int) = CXNullabilityKind.values().find { it.value == value }!! + } + + class Var(override val rawPtr: NativePtr) : CEnumVar() { + companion object : Type(IntVar.size.toInt()) + var value: CXNullabilityKind + get() = byValue(this.reinterpret().value) + set(value) { this.reinterpret().value = value.value } + } +} + typealias CXVirtualFileOverlayVar = CPointerVarOf typealias CXVirtualFileOverlay = CPointer diff --git a/buildSrc/plugins/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy b/buildSrc/plugins/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy index bbf6934349e..75ba57cf6bf 100644 --- a/buildSrc/plugins/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy +++ b/buildSrc/plugins/src/main/groovy/org/jetbrains/kotlin/NativeInteropPlugin.groovy @@ -72,6 +72,10 @@ class NamedNativeInteropConfig implements Named { pkg = value } + void compilerOpts(List values) { + compilerOpts.addAll(values) + } + void compilerOpts(String... values) { compilerOpts.addAll(values) } diff --git a/libclangext/build.gradle b/libclangext/build.gradle new file mode 100644 index 00000000000..2f4a6906c28 --- /dev/null +++ b/libclangext/build.gradle @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: "cpp" +apply plugin: "c" + +ext.isEnabled = isMac() + +model { + components { + clangext(NativeLibrarySpec) { + sources { + cpp { + source.srcDirs "src/main/cpp" + exportedHeaders.srcDirs "src/main/include" + } + } + binaries.withType(StaticLibraryBinarySpec) { binary -> + cppCompiler.args "--std=c++11", "-g", "-fPIC", "-I${llvmDir}/include" + if (isEnabled) { + cppCompiler.args '-DLIBCLANGEXT_ENABLE=1' + } + } + binaries.withType(SharedLibraryBinarySpec) { binary -> + buildable = false + } + } + } +} diff --git a/libclangext/src/main/cpp/ClangExt.cpp b/libclangext/src/main/cpp/ClangExt.cpp new file mode 100644 index 00000000000..10d7ca3a2cd --- /dev/null +++ b/libclangext/src/main/cpp/ClangExt.cpp @@ -0,0 +1,196 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include "clang-c/ext.h" + +using namespace clang; + +#if LIBCLANGEXT_ENABLE + +static CXCursor makeObjCProtocolDeclCXCursor(const ObjCProtocolDecl* decl, CXTranslationUnit translationUnit) { + auto kind = CXCursor_ObjCProtocolDecl; + CXCursor result = { kind, 0, { decl, (void*)(intptr_t) 1, translationUnit } }; + return result; +} + +static const Attr* getCursorAttr(CXCursor cursor) { + return static_cast(cursor.data[1]); +} + +static const Decl *getCursorDecl(CXCursor Cursor) { + return static_cast(Cursor.data[0]); +} + +static const QualType unwrapCXType(CXType type) { + return QualType::getFromOpaquePtr(type.data[0]); +} + +static CXTranslationUnit getTranslationUnit(CXType type) { + return static_cast(type.data[1]); +} + +static ASTUnit* getASTUnit(CXTranslationUnit translationUnit) { + return reinterpret_cast(translationUnit)[1]; +} + +// The functions above are totally libclang-implementation-specific and thus version-dependent. + +static CXTypeAttributes makeCXTypeAttributes(QualType qualType) { + CXTypeAttributes result = { qualType.getAsOpaquePtr() }; + return result; +} + +static QualType unwrapCXTypeAttributes(CXTypeAttributes attributes) { + return QualType::getFromOpaquePtr(attributes.typeOpaquePtr); +} + +#else // LIBCLANGEXT_ENABLE + +static CXTypeAttributes makeCXTypeAttributes() { + CXTypeAttributes result = { nullptr }; + return result; +} + +#endif // LIBCLANGEXT_ENABLE + +extern "C" { + + const char* clang_Cursor_getAttributeSpelling(CXCursor cursor) { +#if LIBCLANGEXT_ENABLE + if (clang_isAttribute(cursor.kind) == 0) { + return nullptr; + } + + return getCursorAttr(cursor)->getSpelling(); +#else + return ""; +#endif + } + + CXTypeAttributes clang_getDeclTypeAttributes(CXCursor cursor) { +#if LIBCLANGEXT_ENABLE + CXType cxType = clang_getCursorType(cursor); + if (clang_isDeclaration(cursor.kind)) { + const Decl *D = getCursorDecl(cursor); + if (D) { + if (const DeclaratorDecl *DD = dyn_cast(D)) + return makeCXTypeAttributes(DD->getType()); + } + } + return makeCXTypeAttributes(QualType()); +#else + return makeCXTypeAttributes(); +#endif + } + + CXTypeAttributes clang_getResultTypeAttributes(CXTypeAttributes typeAttributes) { +#if LIBCLANGEXT_ENABLE + QualType qualType = unwrapCXTypeAttributes(typeAttributes); + if (qualType.isNull()) + return makeCXTypeAttributes(qualType); + + if (const FunctionType *functionType = qualType->getAs()) + return makeCXTypeAttributes(functionType->getReturnType()); + + return makeCXTypeAttributes(QualType()); +#else + return makeCXTypeAttributes(); +#endif + } + + CXTypeAttributes clang_getCursorResultTypeAttributes(CXCursor cursor) { +#if LIBCLANGEXT_ENABLE + if (clang_isDeclaration(cursor.kind)) { + const Decl *decl = getCursorDecl(cursor); + if (const ObjCMethodDecl *methodDecl = dyn_cast_or_null(decl)) + return makeCXTypeAttributes(methodDecl->getReturnType()); + + return clang_getResultTypeAttributes(clang_getDeclTypeAttributes(cursor)); + } + + return makeCXTypeAttributes(QualType()); +#else + return makeCXTypeAttributes(); +#endif + } + + enum CXNullabilityKind clang_Type_getNullabilityKind(CXType type, CXTypeAttributes attributes) { +#if LIBCLANGEXT_ENABLE + CXTranslationUnit translationUnit = getTranslationUnit(type); + ASTContext& astContext = getASTUnit(translationUnit)->getASTContext(); + + QualType qualType = unwrapCXTypeAttributes(attributes); + + auto kind = qualType->getNullability(astContext); + if (!kind.hasValue()) { + return CXNullabilityKind_Unspecified; + } + + switch (*kind) { + case NullabilityKind::NonNull: return CXNullabilityKind_NonNull; + case NullabilityKind::Nullable: return CXNullabilityKind_Nullable; + case NullabilityKind::Unspecified: return CXNullabilityKind_Unspecified; + default: assert(false); + } +#else + return CXNullabilityKind_Unspecified; +#endif + } + + unsigned clang_Type_getNumProtocols(CXType type) { +#if LIBCLANGEXT_ENABLE + QualType qualType = unwrapCXType(type); + if (auto objCObjectPointerType = qualType->getAs()) { + return objCObjectPointerType->getObjectType()->getNumProtocols(); + } +#endif + return 0; + } + + CXCursor clang_Type_getProtocol(CXType type, unsigned index) { +#if LIBCLANGEXT_ENABLE + QualType qualType = unwrapCXType(type); + if (auto objCObjectPointerType = qualType->getAs()) { + auto objectType = objCObjectPointerType->getObjectType(); + unsigned n = objectType->getNumProtocols(); + if (index < n) { + auto protocolDecl = objectType->getProtocol(index); + auto kind = CXCursor_ObjCProtocolDecl; + return makeObjCProtocolDeclCXCursor(protocolDecl, getTranslationUnit(type)); + } + } +#endif + return clang_getNullCursor(); + } + + unsigned clang_Cursor_isObjCInitMethod(CXCursor cursor) { +#if LIBCLANGEXT_ENABLE + if (cursor.kind == CXCursor_ObjCInstanceMethodDecl) { + const Decl *decl = getCursorDecl(cursor); + if (const ObjCMethodDecl *methodDecl = dyn_cast_or_null(decl)) { + return methodDecl->getMethodFamily() == OMF_init; + } + } +#endif + return 0; + } + +} diff --git a/libclangext/src/main/include/clang-c/ext.h b/libclangext/src/main/include/clang-c/ext.h new file mode 100644 index 00000000000..5c4e15b6aaf --- /dev/null +++ b/libclangext/src/main/include/clang-c/ext.h @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +// TODO: the API declared below should eventually be refined and contributed to libclang. + + +// libclang doesn't include the type attributes when constructing `CXType`, +// so attributes have to be represented separately: +typedef struct { + const void* typeOpaquePtr; +} CXTypeAttributes; + +enum CXNullabilityKind { + CXNullabilityKind_Nullable, + CXNullabilityKind_NonNull, + CXNullabilityKind_Unspecified +}; + +#ifdef __cplusplus +extern "C" { +#endif + +const char* clang_Cursor_getAttributeSpelling(CXCursor cursor); + +CXTypeAttributes clang_getDeclTypeAttributes(CXCursor cursor); + +CXTypeAttributes clang_getResultTypeAttributes(CXTypeAttributes typeAttributes); + +CXTypeAttributes clang_getCursorResultTypeAttributes(CXCursor cursor); + +enum CXNullabilityKind clang_Type_getNullabilityKind(CXType type, CXTypeAttributes attributes); + +unsigned clang_Type_getNumProtocols(CXType type); + +CXCursor clang_Type_getProtocol(CXType type, unsigned index); + +unsigned clang_Cursor_isObjCInitMethod(CXCursor cursor); + +#ifdef __cplusplus +} +#endif diff --git a/settings.gradle b/settings.gradle index 5c2c8397916..6eb7bc49fde 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,6 +19,7 @@ include ':Interop:Indexer' include ':Interop:StubGenerator' include ':Interop:Runtime' include ':llvmDebugInfoC' +include ':libclangext' include ':klib' include ':backend.native' include ':runtime'