From 073c6d44ad4392d84d5d116062d8f28678e228e9 Mon Sep 17 00:00:00 2001 From: Corey O'Connor Date: Fri, 25 Aug 2023 17:46:07 -0700 Subject: [PATCH] feature: Support NIR files for file decoding --- .../internal/metals/FileDecoderProvider.scala | 65 +++++++++++++++++-- .../meta/internal/parsing/ClassFinder.scala | 3 + 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/metals/src/main/scala/scala/meta/internal/metals/FileDecoderProvider.scala b/metals/src/main/scala/scala/meta/internal/metals/FileDecoderProvider.scala index 854a62b63d4..7945713a7c7 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/FileDecoderProvider.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/FileDecoderProvider.scala @@ -147,6 +147,9 @@ final class FileDecoderProvider( * * build target: * metalsDecode:file:///workspacePath/buildTargetName.metals-buildtarget + * + * native intermediate representation (NIR): + * metalsDecode:file:///somePath/someFile.scala.nir */ def decodedFileContents(uriAsStr: String): Future[DecoderResponse] = { Try(URI.create(URIEncoderDecoder.encode(uriAsStr))) match { @@ -209,10 +212,7 @@ final class FileDecoderProvider( uri: URI ): Future[DecoderResponse] = { val supportedExtensions = Set( - "javap", - "javap-verbose", - "tasty-decoded", - "cfr", + "javap", "javap-verbose", "tasty-decoded", "cfr", "nir", ) ++ semanticdbExtensions val additionalExtension = uri.toString().split('.').toList.last if (supportedExtensions(additionalExtension)) { @@ -226,6 +226,12 @@ final class FileDecoderProvider( case "javap-verbose" => decodeJavaOrScalaOrClass(path, decodeJavapFromClassFile(true)) case "cfr" => decodeJavaOrScalaOrClass(path, decodeCFRFromClassFile) + case "nir" => + selectClassFromScalaFileAndDecode( + path.toURI, + path, + ClassFinderGranularity.NIR, + )(p => decodeNIR(p.path)) case "tasty-decoded" => decodeTasty(path) case "semanticdb-compact" => Future.successful( @@ -568,6 +574,57 @@ final class FileDecoderProvider( } } + private def decodeNIR( + path: AbsolutePath + ): Future[DecoderResponse] = { + val scalaNativeCLIDependency = + Dependency.of("org.scala-native", "scala-native-cli_2.13", "0.4.10") + val scalaNativePMain = "scala.scalanative.cli.ScalaNativeP" + + // the class finder gets us the .nir under the java built target. How to get the proper native one? + // the replaceAll below assumes a particular layout. + val parent = path.parent + val args = List( + "--verbose", + "--from-path", + path.toString().replaceAll(".jvm", ".native"), + ) + val sbOut = new StringBuilder() + val sbErr = new StringBuilder() + try { + shellRunner + .runJava( + scalaNativeCLIDependency, + scalaNativePMain, + parent, + args, + redirectErrorOutput = false, + s => { + sbOut.append(s) + sbOut.append(Properties.lineSeparator) + }, + s => { + sbErr.append(s) + sbErr.append(Properties.lineSeparator) + }, + propagateError = true, + ) + .map(_ => { + if (sbOut.isEmpty && sbErr.nonEmpty) + DecoderResponse.failed( + path.toURI, + s"$scalaNativeCLIDependency\n$scalaNativePMain\n$parent\n$args\n${sbErr.toString}", + ) + else + DecoderResponse.success(path.toURI, sbOut.toString) + }) + } catch { + case NonFatal(e) => + scribe.error(e.toString()) + Future.successful(DecoderResponse.failed(path.toURI, e)) + } + } + private def decodeCFRFromClassFile( path: AbsolutePath ): Future[DecoderResponse] = { diff --git a/metals/src/main/scala/scala/meta/internal/parsing/ClassFinder.scala b/metals/src/main/scala/scala/meta/internal/parsing/ClassFinder.scala index a6f27fc65c9..fd6d9dc1d79 100644 --- a/metals/src/main/scala/scala/meta/internal/parsing/ClassFinder.scala +++ b/metals/src/main/scala/scala/meta/internal/parsing/ClassFinder.scala @@ -250,16 +250,19 @@ sealed trait ClassFinderGranularity { def isTasty: Boolean = this match { case ClassFiles => false + case NIR => false case Tasty => true } def extension: String = this match { case ClassFiles => ".class" + case NIR => ".nir" case Tasty => ".tasty" } } object ClassFinderGranularity { case object ClassFiles extends ClassFinderGranularity + case object NIR extends ClassFinderGranularity case object Tasty extends ClassFinderGranularity }