diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..8680c845 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +core/src/main/native-image/resources/META-INF/native-image/com/facebook/ktfmt/reachability-metadata.json linguist-generated=true diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index da7d1e22..e73ae47b 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -4,6 +4,9 @@ on: push: paths-ignore: - '**.md' + branches: + - main + - feat/native-image pull_request: paths-ignore: - '**.md' @@ -25,5 +28,22 @@ jobs: with: java-version: ${{ matrix.java }} distribution: zulu + # set-java-home is intentionally 'false': the main build runs on the Zulu JDK from the matrix + # (Java 17/21), so we must not repoint JAVA_HOME at GraalVM 25. setup-graalvm still exports + # GRAALVM_HOME, which the GraalVM Gradle plugin uses to locate the native-image toolchain for the + # :ktfmt:nativeCompile step. This keeps the JVM build on the matrix JDK and the native build on + # GraalVM. + - name: Set up GraalVM + uses: graalvm/setup-graalvm@v1 + with: + java-version: '25.0.1' + distribution: 'graalvm' + github-token: ${{ secrets.GITHUB_TOKEN }} + set-java-home: 'false' + native-image-job-reports: 'true' - name: Build ktfmt run: ./gradlew build --no-daemon + - name: Build Native Image + run: ./gradlew :ktfmt:nativeCompile --stacktrace --no-daemon + - name: Smoke test Native Image + run: ./native_smoke_test.sh diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 0d3d2a8f..e64e07ce 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -22,12 +22,18 @@ plugins { alias(libs.plugins.ktfmt) } +dependencies { implementation(nativeImageLibs.graalvm.gradle.plugin) } + gradlePlugin { plugins { register("ktfmt-file-generator") { id = "ktfmt.ktfmt-file-generator" implementationClass = "com.facebook.ktfmt.GenerateKtfmtFilePlugin" } + register("native-image") { + id = "ktfmt.native-image" + implementationClass = "com.facebook.ktfmt.NativeImagePlugin" + } } } diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index 53b7b771..d2393d2d 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -21,7 +21,10 @@ dependencyResolutionManagement { gradlePluginPortal() } - versionCatalogs { create("libs") { from(files("../gradle/libs.versions.toml")) } } + versionCatalogs { + create("libs") { from(files("../gradle/libs.versions.toml")) } + create("nativeImageLibs") { from(files("../gradle/native-image.versions.toml")) } + } } rootProject.name = "buildSrc" diff --git a/buildSrc/src/main/kotlin/com/facebook/ktfmt/NativeImagePlugin.kt b/buildSrc/src/main/kotlin/com/facebook/ktfmt/NativeImagePlugin.kt new file mode 100644 index 00000000..2f478a2e --- /dev/null +++ b/buildSrc/src/main/kotlin/com/facebook/ktfmt/NativeImagePlugin.kt @@ -0,0 +1,251 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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. + */ + +package com.facebook.ktfmt + +import org.graalvm.buildtools.gradle.dsl.GraalVMExtension +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.api.plugins.JavaApplication +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.jvm.tasks.Jar +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.named +import org.gradle.kotlin.dsl.register + +@Suppress("unused") +class NativeImagePlugin : Plugin { + override fun apply(project: Project) { + project.plugins.apply("application") + project.plugins.apply("org.graalvm.buildtools.native") + + project.extensions.configure { mainClass.set(ENTRYPOINT) } + + project.plugins.withId("java") { configureNativeImage(project) } + } + + private fun configureNativeImage(project: Project) { + val nativeImageLibs = + project.extensions.getByType().named("nativeImageLibs") + + val nativeImageJavacClasspath: Configuration = + project.configurations.create("nativeImageJavacClasspath") { + extendsFrom(project.configurations.getByName("implementation")) + isCanBeResolved = true + } + + project.dependencies.apply { + add("nativeImageJavacClasspath", nativeImageLibs.findLibrary("graalvm-nativeimage").get()) + add("nativeImageClasspath", nativeImageLibs.findLibrary("jline-terminal").get()) + add("nativeImageClasspath", nativeImageLibs.findLibrary("jline-terminal-jansi").get()) + add("nativeImageClasspath", nativeImageLibs.findLibrary("jline-terminal-jna").get()) + add("nativeImageClasspath", nativeImageLibs.findLibrary("jline-terminal-jni").get()) + } + + val nativeImageDir = project.layout.projectDirectory.dir(NATIVE_IMAGE_SRC_DIR) + val javaExtension = project.extensions.getByType() + val nativeImageSourceSet = + javaExtension.sourceSets.create("nativeImage") { + java.srcDir(nativeImageDir.dir("java")) + resources.srcDir(nativeImageDir.dir("resources")) + } + + val compileNativeImageClasses = + project.tasks.register( + "compileNativeImageClasses", + org.gradle.api.tasks.compile.JavaCompile::class, + ) { + group = "build" + description = "Compiles Native Image helper classes" + source = nativeImageSourceSet.java + classpath = nativeImageJavacClasspath + destinationDirectory.set(project.layout.buildDirectory.dir("classes/native-image")) + dependsOn(project.tasks.named("compileJava")) + } + + val nativeImageJar = + project.tasks.register("nativeImageJar", Jar::class) { + group = "build" + description = "Assembles Native Image jar and resources" + dependsOn(compileNativeImageClasses) + from(project.layout.buildDirectory.dir("classes/native-image")) + from(nativeImageSourceSet.resources) + archiveClassifier.set("nativeimage") + } + + project.tasks.named("nativeCompile") { dependsOn(compileNativeImageClasses, nativeImageJar) } + + configureGraalvmNative(project, nativeImageJar) + } + + private fun configureGraalvmNative( + project: Project, + nativeImageJar: org.gradle.api.tasks.TaskProvider, + ) { + val nativeRelease = project.findProperty("ktfmt.native.release") == "true" + val nativeTarget = project.findProperty("ktfmt.native.target") ?: "compatibility" + val nativeGc = project.findProperty("ktfmt.native.gc") ?: "serial" + val nativeDebug = project.findProperty("ktfmt.native.debug") == "true" + val enableLto = project.findProperty("ktfmt.native.lto") == "true" + val muslSysroot = + (project.findProperty("ktfmt.native.muslHome") ?: System.getenv("MUSL_HOME"))?.toString() + val preferMusl = + (project.findProperty("ktfmt.native.musl") == "true").also { enabled -> + require(!enabled || muslSysroot != null) { + "When `ktfmt.native.musl` is true, -Pktfmt.native.muslHome or MUSL_HOME must be set to the Musl sysroot. " + + "See https://www.graalvm.org/latest/reference-manual/native-image/guides/build-static-executables/" + } + } + val preferSmol = project.findProperty("ktfmt.native.smol") == "true" + val nativeOpt = + when (val opt = project.findProperty("ktfmt.native.opt")) { + null -> + when { + preferSmol -> "s" + nativeRelease -> "3" + else -> "b" + } + else -> opt + } + + project.extensions.configure("graalvmNative") { + binaries.named("main") { + imageName.set("ktfmt") + mainClass.set(ENTRYPOINT) + classpath( + project.files( + nativeImageJar.flatMap { it.archiveFile }, + project.tasks.named("jar", Jar::class).flatMap { it.archiveFile }, + project.configurations.getByName("compileClasspath"), + project.configurations.getByName("runtimeClasspath"), + project.configurations.getByName("nativeImageClasspath"), + ) + ) + buildArgs( + buildNativeImageArgs( + project, + nativeOpt, + nativeTarget, + nativeDebug, + nativeGc, + enableLto, + preferMusl, + muslSysroot, + ) + ) + } + } + } + + private fun buildNativeImageArgs( + project: Project, + nativeOpt: Any, + nativeTarget: Any, + nativeDebug: Boolean, + nativeGc: Any, + enableLto: Boolean, + preferMusl: Boolean, + muslSysroot: String?, + ): List = buildList { + add("-O$nativeOpt") + add("-march=$nativeTarget") + if (nativeDebug) { + add("-g") + add("-H:+SourceLevelDebug") + } + + add("--no-fallback") + add("--gc=$nativeGc") + add("--future-defaults=all") + add("--link-at-build-time=com.facebook") + add("--initialize-at-build-time=com.facebook") + add("--add-opens=java.base/java.util=ALL-UNNAMED") + add("--color=always") + add("-H:+ReportExceptionStackTraces") + add("-H:-UseContainerSupport") + add("-R:+InstallSegfaultHandler") + add("-H:+UnlockExperimentalVMOptions") + add("-H:-ReduceImplicitExceptionStackTraceInformation") + add("-H:-UnlockExperimentalVMOptions") + add("-J--enable-native-access=ALL-UNNAMED") + add("-J--illegal-native-access=allow") + add("-J--sun-misc-unsafe-memory-access=allow") + + if (enableLto) { + add("--native-compiler-options=-flto") + add("-H:NativeLinkerOption=-flto") + } + if (preferMusl) { + add("-H:NativeLinkerOption=-L${muslSysroot}/lib") + } + + addLinesFromFile(project, "initialize-at-build-time.txt") { "--initialize-at-build-time=$it" } + addLinesFromFile(project, "initialize-at-run-time.txt") { "--initialize-at-run-time=$it" } + + when (System.getProperty("os.name")) { + "Linux" -> + when (normalizeArch(System.getProperty("os.arch"))) { + "x64" -> + if (preferMusl) { + addAll(listOf("--static", "--libc=musl", "-H:+StaticLibStdCpp")) + } else { + add("--static-nolibc") + } + else -> add("--static-nolibc") + } + "Mac OS X" -> add("--static-nolibc") + } + } + + /** Canonicalizes [java.lang.System.getProperty]("os.arch") values that vary across JVMs. */ + private fun normalizeArch(arch: String?): String = + when (arch) { + "amd64", + "x86_64" -> "x64" + "aarch64", + "arm64" -> "aarch64" + else -> arch.orEmpty() + } + + private fun MutableList.addLinesFromFile( + project: Project, + fileName: String, + mapper: (String) -> String, + ) { + val file = project.layout.projectDirectory.dir(NATIVE_IMAGE_SRC_DIR).file(fileName).asFile + if (!file.exists()) { + throw GradleException("Native Image configuration file not found: $file") + } + file + .useLines { lines -> + lines + .map { it.trim() } + .filter { it.isNotEmpty() && !it.startsWith("#") } + .map(mapper) + .toList() + } + .also { addAll(it) } + } + + private companion object { + const val ENTRYPOINT = "com.facebook.ktfmt.cli.Main" + const val NATIVE_IMAGE_SRC_DIR = "src/main/native-image" + } +} diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 32bd429c..4e886692 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -28,6 +28,7 @@ plugins { id("maven-publish") id("signing") id("ktfmt.ktfmt-file-generator") + id("ktfmt.native-image") } repositories { diff --git a/core/src/main/java/com/facebook/ktfmt/format/Parser.kt b/core/src/main/java/com/facebook/ktfmt/format/Parser.kt index 799f2c06..850574bc 100644 --- a/core/src/main/java/com/facebook/ktfmt/format/Parser.kt +++ b/core/src/main/java/com/facebook/ktfmt/format/Parser.kt @@ -43,9 +43,7 @@ object Parser { * from [KotlinCoreEnvironment.createForProduction]: * https://github.com/JetBrains/kotlin/blob/master/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt#L544 */ - val env: KotlinCoreEnvironment - - init { + val env: KotlinCoreEnvironment by lazy { // To hide annoying warning on Windows System.setProperty("idea.use.native.fs.for.win", "false") val disposable = Disposer.newDisposable() @@ -54,13 +52,12 @@ object Parser { CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY, PrintingMessageCollector(System.err, PLAIN_RELATIVE_PATHS, false), ) - env = - @Suppress("OPT_IN_USAGE_ERROR") // KotlinCoreEnvironment.createForProduction - KotlinCoreEnvironment.createForProduction( - disposable, - configuration, - EnvironmentConfigFiles.JVM_CONFIG_FILES, - ) + @Suppress("OPT_IN_USAGE_ERROR") // KotlinCoreEnvironment.createForProduction + KotlinCoreEnvironment.createForProduction( + disposable, + configuration, + EnvironmentConfigFiles.JVM_CONFIG_FILES, + ) } fun parse(code: String): KtFile { diff --git a/core/src/main/native-image/initialize-at-build-time.txt b/core/src/main/native-image/initialize-at-build-time.txt new file mode 100644 index 00000000..b58cc56e --- /dev/null +++ b/core/src/main/native-image/initialize-at-build-time.txt @@ -0,0 +1,10 @@ +com.google.common.collect.SingletonImmutableList +kotlin.Function +kotlin.KotlinVersion +kotlin.SynchronizedLazyImpl +kotlin.UNINITIALIZED_VALUE +kotlin.collections.AbstractList$Companion +kotlin.collections.EmptyMap +kotlin.enums.EnumEntriesList +kotlin.jvm.functions.Function1 +kotlin.text.Regex diff --git a/core/src/main/native-image/initialize-at-run-time.txt b/core/src/main/native-image/initialize-at-run-time.txt new file mode 100644 index 00000000..a7d9b4fc --- /dev/null +++ b/core/src/main/native-image/initialize-at-run-time.txt @@ -0,0 +1,12 @@ +# Some ktfmt classes build run-time-only object graphs in their (ec4j config types, Kotlin +# compiler PSI stub element types); keep them out of the build-time image heap. The rest of +# com.facebook is initialized at build time for startup speed. +com.facebook.ktfmt.cli.EditorConfigResolver +com.facebook.ktfmt.util.CompatibilityUtilsKt +kotlin.random.AbstractPlatformRandom +kotlin.random.Random +kotlin.random.Random$Default +kotlin.random.RandomKt +kotlin.random.XorWowRandom +kotlin.random.jdk8.PlatformThreadLocalRandom +kotlin.uuid.SecureRandomHolder diff --git a/core/src/main/native-image/java/com/facebook/ktfmt/nativeImage/KotlinCoreEnvironmentCompanion.java b/core/src/main/native-image/java/com/facebook/ktfmt/nativeImage/KotlinCoreEnvironmentCompanion.java new file mode 100644 index 00000000..448d713b --- /dev/null +++ b/core/src/main/native-image/java/com/facebook/ktfmt/nativeImage/KotlinCoreEnvironmentCompanion.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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. + */ + +package com.facebook.ktfmt.nativeImage; + +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.annotate.TargetElement; +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; +import org.jetbrains.kotlin.config.CompilerConfiguration; + +@SuppressWarnings({"unused"}) +@TargetClass(KotlinCoreEnvironment.Companion.class) +final class KotlinCoreEnvironmentCompanion { + @Substitute + @TargetElement(name = "registerApplicationExtensionPointsAndExtensionsFrom") + private void stubbedRegisterApplicationExtensionPointsAndExtensionsFrom( + CompilerConfiguration configuration, String configFilePath) { + // Nothing at this time. + } +} diff --git a/core/src/main/native-image/resources/META-INF/native-image/com/facebook/ktfmt/reachability-metadata.json b/core/src/main/native-image/resources/META-INF/native-image/com/facebook/ktfmt/reachability-metadata.json new file mode 100644 index 00000000..8bc3bd1b --- /dev/null +++ b/core/src/main/native-image/resources/META-INF/native-image/com/facebook/ktfmt/reachability-metadata.json @@ -0,0 +1,2247 @@ +{ + "reflection": [ + { + "type": "android.os.Build$VERSION" + }, + { + "type": "java.util.concurrent.ForkJoinTask", + "fields": [ + { + "name": "aux" + }, + { + "name": "status" + } + ] + }, + { + "type": "java.util.concurrent.atomic.AtomicBoolean", + "fields": [ + { + "name": "value" + } + ] + }, + { + "type": "java.util.concurrent.atomic.AtomicReference", + "fields": [ + { + "name": "value" + } + ] + }, + { + "type": "kotlin.SafePublicationLazyImpl", + "fields": [ + { + "name": "_value" + } + ] + }, + { + "type": "kotlin.jvm.internal.DefaultConstructorMarker" + }, + { + "type": "kotlin.reflect.jvm.internal.ReflectionFactoryImpl", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "type": "kotlinx.coroutines.CancellableContinuationImpl" + }, + { + "type": "kotlinx.coroutines.EventLoopImplBase", + "fields": [ + { + "name": "_delayed$volatile" + }, + { + "name": "_isCompleted$volatile" + }, + { + "name": "_queue$volatile" + } + ] + }, + { + "type": "kotlinx.coroutines.JobSupport", + "fields": [ + { + "name": "_parentHandle$volatile" + }, + { + "name": "_state$volatile" + } + ] + }, + { + "type": "kotlinx.coroutines.JobSupport$Finishing" + }, + { + "type": "kotlinx.coroutines.channels.BufferedChannel" + }, + { + "type": "kotlinx.coroutines.internal.AtomicOp" + }, + { + "type": "kotlinx.coroutines.internal.ConcurrentLinkedListNode" + }, + { + "type": "kotlinx.coroutines.internal.DispatchedContinuation", + "fields": [ + { + "name": "_reusableCancellableContinuation$volatile" + } + ] + }, + { + "type": "kotlinx.coroutines.internal.LimitedDispatcher", + "fields": [ + { + "name": "runningWorkers$volatile" + } + ] + }, + { + "type": "kotlinx.coroutines.internal.LockFreeLinkedListNode" + }, + { + "type": "kotlinx.coroutines.internal.LockFreeTaskQueue", + "fields": [ + { + "name": "_cur$volatile" + } + ] + }, + { + "type": "kotlinx.coroutines.internal.LockFreeTaskQueueCore", + "fields": [ + { + "name": "_next$volatile" + }, + { + "name": "_state$volatile" + } + ] + }, + { + "type": "kotlinx.coroutines.internal.Segment" + }, + { + "type": "kotlinx.coroutines.scheduling.CoroutineScheduler", + "fields": [ + { + "name": "_isTerminated$volatile" + }, + { + "name": "controlState$volatile" + }, + { + "name": "parkedWorkersStack$volatile" + } + ] + }, + { + "type": "kotlinx.coroutines.scheduling.CoroutineScheduler$Worker" + }, + { + "type": "kotlinx.coroutines.scheduling.WorkQueue" + }, + { + "type": "org.jetbrains.kotlin.cli.common.CompilerSystemProperties" + }, + { + "type": "org.jetbrains.kotlin.com.intellij.codeInsight.ContainerProvider" + }, + { + "type": "org.jetbrains.kotlin.com.intellij.codeInsight.multiverse.CodeInsightContextProvider" + }, + { + "type": "org.jetbrains.kotlin.com.intellij.codeInsight.multiverse.MultiverseEnabler" + }, + { + "type": "org.jetbrains.kotlin.com.intellij.openapi.application.impl.ModalityStateEx", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.openapi.extensions.impl.ExtensionPointImpl", + "fields": [ + { + "name": "keyMapperToCache" + }, + { + "name": "listeners" + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.openapi.fileTypes.BinaryFileTypeDecompilers", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.openapi.fileTypes.LanguageFileType", + "methods": [ + { + "name": "extractCharsetFromFileContent", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.openapi.project.Project", + "org.jetbrains.kotlin.com.intellij.openapi.vfs.VirtualFile", + "java.lang.CharSequence" + ] + }, + { + "name": "extractCharsetFromFileContent", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.openapi.project.Project", + "org.jetbrains.kotlin.com.intellij.openapi.vfs.VirtualFile", + "java.lang.String" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.openapi.util.SimpleModificationTracker", + "fields": [ + { + "name": "myCounter" + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.openapi.vfs.PersistentFSConstants", + "fields": [ + { + "name": "ourMaxIntellisenseFileSize" + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.psi.LanguageSubstitutors", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.psi.PsiElementFinder" + }, + { + "type": "org.jetbrains.kotlin.com.intellij.psi.SingleRootFileViewProvider", + "fields": [ + { + "name": "myPsiFile" + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.psi.compiled.ClassFileDecompilers", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.psi.compiled.ClassFileDecompilers$Decompiler" + }, + { + "type": "org.jetbrains.kotlin.com.intellij.psi.impl.source.JavaFileElementType", + "methods": [ + { + "name": "getExternalId", + "parameterTypes": [] + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement", + "fields": [ + { + "name": "myWrapper" + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.psi.stubs.StubBase", + "fields": [ + { + "name": "myPsi" + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.util.ConcurrentLongObjectHashMap", + "fields": [ + { + "name": "baseCount" + }, + { + "name": "cellsBusy" + }, + { + "name": "sizeCtl" + }, + { + "name": "transferIndex" + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.util.ConcurrentLongObjectHashMap$CounterCell", + "fields": [ + { + "name": "value" + } + ] + }, + { + "type": "org.jetbrains.kotlin.com.intellij.util.KeyedLazyInstanceEP" + }, + { + "type": "org.jetbrains.kotlin.com.intellij.util.QueryExecutor" + }, + { + "type": "org.jetbrains.kotlin.extensions.CompilerConfigurationExtension" + }, + { + "type": "org.jetbrains.kotlin.extensions.CompilerConfigurationExtension[]" + }, + { + "type": "org.jetbrains.kotlin.idea.KotlinFileType" + }, + { + "type": "org.jetbrains.kotlin.kdoc.psi.impl.KDocLink[]" + }, + { + "type": "org.jetbrains.kotlin.kdoc.psi.impl.KDocName", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.kdoc.psi.impl.KDocName[]" + }, + { + "type": "org.jetbrains.kotlin.kdoc.psi.impl.KDocSection", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.kdoc.psi.impl.KDocSection[]" + }, + { + "type": "org.jetbrains.kotlin.kdoc.psi.impl.KDocTag", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.kdoc.psi.impl.KDocTag[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtAnnotatedExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtAnnotation", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtAnnotationEntry", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinAnnotationEntryStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtAnnotationEntry[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtAnnotationUseSiteTarget", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinAnnotationUseSiteTargetStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtAnnotationUseSiteTarget[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtAnnotation[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtArrayAccessExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtBackingField", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinBackingFieldStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtBackingField[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtBinaryExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinBlockStringTemplateEntryStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtBreakExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtCallExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtCallExpression[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtCallableReferenceExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtCatchClause", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtClass", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinClassStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtClassBody", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtClassBody[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtClassInitializer", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtClassInitializer[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtClassLiteralExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinClassLiteralExpressionStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtClassLiteralExpression[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtClass[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtCollectionLiteralExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinCollectionLiteralExpressionStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtCollectionLiteralExpression[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtConstantExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinConstantExpressionStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtConstantExpression[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtConstructorCalleeExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtConstructorCalleeExpression[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtConstructorDelegationCall", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtConstructorDelegationReferenceExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtContainerNode", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtContainerNodeForControlStructureBody", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtContextReceiver", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinContextReceiverStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtContextReceiverList", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtContextReceiverList[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtContextReceiver[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtContinueExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtContractEffect", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinContractEffectStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtContractEffectList", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtContractEffectList[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtContractEffect[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtDeclarationModifierList", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinModifierListStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtDeclarationModifierList[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtDestructuringDeclaration", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtDoWhileExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtDotQualifiedExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtDotQualifiedExpression[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtDynamicType", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtDynamicType[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtEnumEntry", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtEnumEntrySuperclassReferenceExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinEnumEntrySuperclassReferenceExpressionStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtEnumEntrySuperclassReferenceExpression[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtEnumEntry[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtEscapeStringTemplateEntry", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderWithTextStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtEscapeStringTemplateEntry[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtFileAnnotationList", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtFileAnnotationList[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtFinallySection", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtForExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtFunctionLiteral", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtFunctionType", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinFunctionTypeStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtFunctionTypeReceiver", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtFunctionTypeReceiver[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtFunctionType[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtIfExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtImportAlias", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinImportAliasStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtImportAlias[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtImportDirective", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinImportDirectiveStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtImportDirective[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtImportList", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtImportList[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtInitializerList", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtInitializerList[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtIntersectionType", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtIntersectionType[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtIsExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtLabelReferenceExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtLabeledExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtLambdaArgument", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinValueArgumentStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtLambdaArgument[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderWithTextStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtNameReferenceExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinNameReferenceExpressionStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtNameReferenceExpression[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtNamedFunction", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinFunctionStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtNamedFunction[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtNullableType", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtNullableType[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtObjectDeclaration", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinObjectStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtObjectDeclaration[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtObjectLiteralExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtOperationReferenceExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtPackageDirective", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtPackageDirective[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtParameter", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinParameterStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtParameterList", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtParameterList[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtParameter[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtParenthesizedExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtPostfixExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtPrefixExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtPrimaryConstructor", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinConstructorStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtPrimaryConstructor[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtProperty", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPropertyStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtPropertyAccessor", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPropertyAccessorStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtPropertyAccessor[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtPropertyDelegate", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtProperty[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtReturnExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtSafeQualifiedExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtScript", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinScriptStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtScriptInitializer", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtScriptInitializer[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtScript[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtSecondaryConstructor", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinConstructorStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtSecondaryConstructor[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtSimpleNameStringTemplateEntry", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderWithTextStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtSimpleNameStringTemplateEntry[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtStringInterpolationPrefix", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinStringInterpolationPrefixStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtStringInterpolationPrefix[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtStringTemplateExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtStringTemplateExpression[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtSuperExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtSuperTypeCallEntry", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtSuperTypeCallEntry[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtSuperTypeEntry", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtSuperTypeEntry[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtSuperTypeList", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtSuperTypeList[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtThisExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtThrowExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtTryExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeAlias", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinTypeAliasStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeAlias[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeArgumentList", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeArgumentList[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeConstraint", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeConstraintList", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeConstraintList[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeConstraint[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeParameter", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinTypeParameterStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeParameterList", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeParameterList[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeParameter[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeProjection", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinTypeProjectionStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeProjection[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeReference", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtTypeReference[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtUserType", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinUserTypeStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtUserType[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtValueArgument", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinValueArgumentStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtValueArgumentList", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtValueArgumentList[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtValueArgumentName", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + }, + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtValueArgumentName[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtValueArgument[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtWhenConditionInRange", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtWhenConditionIsPattern", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtWhenConditionWithExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtWhenCondition[]" + }, + { + "type": "org.jetbrains.kotlin.psi.KtWhenEntry", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtWhenEntryGuard", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtWhenExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.KtWhileExpression", + "methods": [ + { + "name": "", + "parameterTypes": [ + "org.jetbrains.kotlin.com.intellij.lang.ASTNode" + ] + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.impl.KotlinElementTypeProviderImpl", + "fields": [ + { + "name": "INSTANCE" + } + ] + }, + { + "type": "org.jetbrains.kotlin.psi.stubs.elements.KtFileElementType", + "methods": [ + { + "name": "getExternalId", + "parameterTypes": [] + } + ] + }, + { + "type": "sun.misc.Unsafe", + "allDeclaredFields": true, + "methods": [ + { + "name": "arrayBaseOffset", + "parameterTypes": [ + "java.lang.Class" + ] + }, + { + "name": "arrayIndexScale", + "parameterTypes": [ + "java.lang.Class" + ] + }, + { + "name": "compareAndSwapInt", + "parameterTypes": [ + "java.lang.Object", + "long", + "int", + "int" + ] + }, + { + "name": "compareAndSwapLong", + "parameterTypes": [ + "java.lang.Object", + "long", + "long", + "long" + ] + }, + { + "name": "compareAndSwapObject", + "parameterTypes": [ + "java.lang.Object", + "long", + "java.lang.Object", + "java.lang.Object" + ] + }, + { + "name": "copyMemory", + "parameterTypes": [ + "java.lang.Object", + "long", + "java.lang.Object", + "long", + "long" + ] + }, + { + "name": "getAndAddInt", + "parameterTypes": [ + "java.lang.Object", + "long", + "int" + ] + }, + { + "name": "getObjectVolatile", + "parameterTypes": [ + "java.lang.Object", + "long" + ] + }, + { + "name": "invokeCleaner", + "parameterTypes": [ + "java.nio.ByteBuffer" + ] + }, + { + "name": "objectFieldOffset", + "parameterTypes": [ + "java.lang.reflect.Field" + ] + }, + { + "name": "putObjectVolatile", + "parameterTypes": [ + "java.lang.Object", + "long", + "java.lang.Object" + ] + } + ] + }, + { + "type": { + "proxy": [ + "org.jetbrains.kotlin.com.intellij.openapi.command.CommandListener" + ] + } + }, + { + "type": { + "proxy": [ + "org.jetbrains.kotlin.com.intellij.openapi.vfs.VirtualFileListener" + ] + } + }, + { + "type": { + "proxy": [ + "org.jetbrains.kotlin.com.intellij.psi.util.PsiModificationTracker$Listener" + ] + } + } + ], + "resources": [ + { + "glob": "org/jetbrains/kotlin/cli/common/CompilerSystemProperties.class" + }, + { + "glob": "pluginsCompatibleWithK2Mode.txt" + } + ] +} \ No newline at end of file diff --git a/core/src/test/java/com/facebook/ktfmt/format/ParserTest.kt b/core/src/test/java/com/facebook/ktfmt/format/ParserTest.kt new file mode 100644 index 00000000..30d67e96 --- /dev/null +++ b/core/src/test/java/com/facebook/ktfmt/format/ParserTest.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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. + */ + +package com.facebook.ktfmt.format + +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class ParserTest { + /** + * [Parser.env] is initialized lazily, so the `idea.use.native.fs.for.win` property (which + * suppresses a noisy IntelliJ filesystem warning on Windows) is now set on first parse rather + * than at class load. This guards that the property is still applied before the environment is + * built, regardless of platform, so the Windows code path keeps working. + */ + @Test + fun `parsing sets idea_use_native_fs_for_win to false`() { + Parser.parse("val a = 1") + assertThat(System.getProperty("idea.use.native.fs.for.win")).isEqualTo("false") + } +} diff --git a/core/src/test/java/com/facebook/ktfmt/nativeImage/NativeImageSubstitutionTest.kt b/core/src/test/java/com/facebook/ktfmt/nativeImage/NativeImageSubstitutionTest.kt new file mode 100644 index 00000000..0400d162 --- /dev/null +++ b/core/src/test/java/com/facebook/ktfmt/nativeImage/NativeImageSubstitutionTest.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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. + */ + +package com.facebook.ktfmt.nativeImage + +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +/** + * Guards the GraalVM `@Substitute` in `core/src/main/native-image` that replaces + * `KotlinCoreEnvironment.Companion.registerApplicationExtensionPointsAndExtensionsFrom`. The + * substitution is matched by name and parameter types at native-image build time; a mismatch is + * silently ignored and only surfaces as a native runtime failure. This JVM test runs on every build + * so a Kotlin compiler upgrade that changes the signature fails here first, before the native image + * breaks. + */ +@RunWith(JUnit4::class) +class NativeImageSubstitutionTest { + @Test + fun `substituted Kotlin compiler method still exists with expected signature`() { + // Throws NoSuchMethodException (failing the test) if the upstream signature changes. + KotlinCoreEnvironment.Companion::class + .java + .getDeclaredMethod( + "registerApplicationExtensionPointsAndExtensionsFrom", + CompilerConfiguration::class.java, + String::class.java, + ) + } +} diff --git a/gradle/native-image.versions.toml b/gradle/native-image.versions.toml new file mode 100644 index 00000000..c47c77d9 --- /dev/null +++ b/gradle/native-image.versions.toml @@ -0,0 +1,12 @@ +[versions] +org-graalvm = "25.0.1" +graalvmPlugin = "0.11.1" +org-jline = "3.30.6" + +[libraries] +graalvm-gradle-plugin = { module = "org.graalvm.buildtools:native-gradle-plugin", version.ref = "graalvmPlugin" } +graalvm-nativeimage = { module = "org.graalvm.nativeimage:svm", version.ref = "org-graalvm" } +jline-terminal = { module = "org.jline:jline-terminal", version.ref = "org-jline" } +jline-terminal-jansi = { module = "org.jline:jline-terminal-jansi", version.ref = "org-jline" } +jline-terminal-jna = { module = "org.jline:jline-terminal-jna", version.ref = "org-jline" } +jline-terminal-jni = { module = "org.jline:jline-terminal-jni", version.ref = "org-jline" } diff --git a/native_smoke_test.sh b/native_smoke_test.sh new file mode 100755 index 00000000..9d597bae --- /dev/null +++ b/native_smoke_test.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# 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. + +# Smoke-tests a ktfmt native binary: confirms it runs and actually formats Kotlin without crashing. +# This exercises the reflection-heavy parser/formatter path that a bare `--version` check would miss +# (e.g. stale GraalVM reachability metadata). +# +# Run from the repo root. +# Usage: ./native_smoke_test.sh [path-to-ktfmt-binary] +set -euo pipefail + +bin="${1:-./core/build/native/nativeCompile/ktfmt}" +if [[ ! -e "$bin" && -e "${bin}.exe" ]]; then + bin="${bin}.exe" +fi +if [[ ! -x "$bin" ]]; then + echo "ktfmt native binary not found (or not executable) at: $bin" >&2 + echo "Pass the binary path as the first argument, or build it first with:" >&2 + echo " ./gradlew :ktfmt:nativeCompile" >&2 + exit 1 +fi + +# A format crash is almost always stale GraalVM reachability metadata. +stale_hint() { + echo "native ktfmt crashed while formatting; the GraalVM reachability metadata may be stale." >&2 + exit 1 +} + +"$bin" --version + +# Representative constructs (enum/class/lambda) that drive Kotlin-compiler PSI reflection. +sample=$'enum class E { A, B }\nclass Foo(val a: Int) { fun bar() = listOf(1, 2).map { it * 2 } }' +formatted="$(printf '%s\n' "$sample" | "$bin" -)" || stale_hint +printf '%s\n' "$formatted" +[[ -n "$formatted" ]] || stale_hint + +# Idempotency: re-formatting the output must be a no-op. +reformatted="$(printf '%s\n' "$formatted" | "$bin" -)" || stale_hint +[[ "$formatted" == "$reformatted" ]] || { + echo 'native ktfmt output is not idempotent' >&2 + exit 1 +} + +# Broader coverage: formatting ktfmt's own sources drives many more Kotlin constructs, and thus +# reflective paths, than the snippet above. Skipped when the sources aren't present. +if [[ -d core/src/main ]]; then + "$bin" -n core/src/main core/src/test >/dev/null || stale_hint +fi + +echo "Native smoke test passed." diff --git a/settings.gradle.kts b/settings.gradle.kts index ea48f265..d1ad3010 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -34,5 +34,6 @@ dependencyResolutionManagement { val ktfmtVersion = providers.gradleProperty("ktfmt.version").get() version("ktfmt", ktfmtVersion) } + create("nativeImageLibs") { from(files("gradle/native-image.versions.toml")) } } }