Skip to content

Commit cfb1477

Browse files
authored
Add option for NIR file deserialization to scala-native-p (#8)
* Add option for NIR file deserialization to scala-native-p * Add simple test for file deserialization * Address review comments * Remove repeated iterations
1 parent 6f2e418 commit cfb1477

File tree

3 files changed

+83
-11
lines changed

3 files changed

+83
-11
lines changed

cli/src/main/scala/scala/scalanative/cli/ScalaNativeP.scala

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import scala.scalanative.cli.options._
99
import scala.scalanative.nir.Global
1010
import scala.scalanative.build.Config
1111
import scala.scalanative.linker.ClassLoader
12+
import java.nio.file.Path
13+
import scala.scalanative.io.VirtualDirectory
14+
import scala.scalanative.nir.serialization.deserializeBinary
15+
import scala.scalanative.nir.Defn
16+
import scala.annotation.tailrec
17+
import java.nio.ByteBuffer
1218

1319
object ScalaNativeP extends CaseApp[POptions] {
1420

@@ -19,7 +25,10 @@ object ScalaNativeP extends CaseApp[POptions] {
1925
}
2026

2127
if (args.all.isEmpty) {
22-
System.err.println("Required NIR file not specified.")
28+
if (options.fromPath)
29+
System.err.println("Required NIR file not specified.")
30+
else
31+
System.err.println("Required class/object not specified.")
2332
exit(1)
2433
}
2534

@@ -32,29 +41,81 @@ object ScalaNativeP extends CaseApp[POptions] {
3241
System.err.println(s"Ignoring non existing path: $path")
3342
}
3443

44+
if (options.fromPath) printFromFiles(classpath, args.all)
45+
else printFromNames(classpath, args.all)
46+
}
47+
48+
private def printFromNames(
49+
classpath: List[Path],
50+
args: Seq[String]
51+
): Unit = {
3552
Scope { implicit scope =>
3653
val classLoader =
3754
ClassLoader.fromDisk {
3855
Config.empty.withClassPath(classpath)
3956
}
4057

4158
for {
42-
fileName <- args.all
59+
className <- args
4360
} {
44-
classLoader.load(Global.Top(fileName)) match {
45-
case Some(defns) =>
46-
defns
47-
.sortBy(_.name.mangle)
48-
.foreach { d =>
49-
println(d.show)
50-
println()
51-
}
52-
case None => fail(s"Not found class/object with name `${fileName}`")
61+
classLoader.load(Global.Top(className)) match {
62+
case Some(defns) => printNIR(defns)
63+
case None => fail(s"Not found class/object with name `${className}`")
5364
}
5465
}
5566
}
5667
}
5768

69+
private def printFromFiles(
70+
classpath: List[Path],
71+
args: Seq[String]
72+
): Unit = {
73+
74+
Scope { implicit scope =>
75+
val cp = classpath.toStream.map(VirtualDirectory.real(_))
76+
def virtualDirPathMatches(
77+
virtualPath: Path,
78+
regularPath: Path
79+
): Boolean = {
80+
// Paths in jars are always using Unix path denotation
81+
val relativeInJar = virtualPath.toString().stripPrefix("/")
82+
relativeInJar == regularPath.toString()
83+
}
84+
@tailrec
85+
def findAndRead(
86+
classpath: Stream[VirtualDirectory],
87+
path: Path
88+
): Option[ByteBuffer] = {
89+
classpath match {
90+
case Stream.Empty => None
91+
case dir #:: tail =>
92+
val found = dir.files
93+
.find(virtualDirPathMatches(_, path))
94+
.map(dir.read(_))
95+
if (found.isEmpty) findAndRead(tail, path)
96+
else found
97+
}
98+
}
99+
for {
100+
fileName <- args
101+
path = Paths.get(fileName).normalize()
102+
content <- findAndRead(cp, path)
103+
.orElse(fail(s"Not found file with name `${fileName}`"))
104+
} {
105+
val defns = deserializeBinary(content, fileName)
106+
printNIR(defns)
107+
}
108+
}
109+
}
110+
111+
private def printNIR(defns: Seq[Defn]) =
112+
defns
113+
.sortBy(_.name.mangle)
114+
.foreach { d =>
115+
println(d.show)
116+
println()
117+
}
118+
58119
private def fail(msg: String): Nothing = {
59120
Console.err.println(msg)
60121
exit(1)

cli/src/main/scala/scala/scalanative/cli/options/POptions.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ case class POptions(
99
@ExtraName("cp")
1010
@ValueDescription("<path>")
1111
classpath: List[String] = "." :: Nil,
12+
@HelpMessage(
13+
"Instead of passing class/object names, pass NIR file paths."
14+
)
15+
fromPath: Boolean = false,
1216
@Recurse
1317
misc: MiscOptions
1418
)

cli/src/sbt-test/integration/standalone/test

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ $ exists Foo$.nir
99

1010
-> runScript scala-native-p not.existing.Class
1111

12+
> runScript scala-native-p --from-path Foo.nir
13+
-> runScript scala-native-p --from-path notExisting.nir
14+
15+
> runExec jar cf inside.jar Foo.class Foo.nir Foo$.class Foo$.nir
16+
> runScript scala-native-p --cp inside.jar --from-path Foo.nir Foo$.nir
17+
-> runScript scala-native-p --cp inside.jar --from-path Main$.nir
18+
1219
> runScript scala-native-ld --main Main . -o scala-native-out.exe -v -v
1320
$ exists scala-native-out.exe
1421
> runExec ./scala-native-out.exe

0 commit comments

Comments
 (0)