Skip to content

Commit

Permalink
fix predef handling for repl, adjust some internal types
Browse files Browse the repository at this point in the history
  • Loading branch information
mpollmeier committed Jun 12, 2024
1 parent 27ccead commit 64e6566
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 39 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Prerequisite: jdk11+
* [Attach a debugger (remote jvm debug)](#attach-a-debugger-remote-jvm-debug)
- [Server mode](#server-mode)
- [Embed into your own project](#embed-into-your-own-project)
- [Global predef file: `~/.srp.sc`](#global-predef-file-srpsc)
- [Global predef file: `~/.scala-repl-pp.sc`](#global-predef-file-scala-repl-ppsc)
- [Verbose mode](#verbose-mode)
- [Inherited classpath](#inherited-classpath)
- [Parameters cheat sheet: the most important ones](#parameters-cheat-sheet-the-most-important-ones)
Expand Down Expand Up @@ -479,11 +479,11 @@ stringcalc> add(One, Two)
val res0: stringcalc.Number = Number(3)
```

## Global predef file: `~/.srp.sc`
Code that should be available across all srp sessions can be written into your local `~/.srp.sc`.
## Global predef file: `~/.scala-repl-pp.sc`
Code that should be available across all srp sessions can be written into your local `~/.scala-repl-pp.sc`.

```
echo 'def bar = 90' > ~/.srp.sc
echo 'def bar = 90' > ~/.scala-repl-pp.sc
echo 'def baz = 91' > script1.sc
echo 'def bam = 92' > script2.sc
Expand Down
5 changes: 3 additions & 2 deletions core/src/main/scala/replpp/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,11 @@ package object replpp {

/** precompile given predef files (if any) and update Config to include the results in the classpath */
def precompilePredefFiles(config: Config): Config = {
if (config.predefFiles.nonEmpty) {
val allPredefFiles = (config.predefFiles :+ globalPredefFile).filter(Files.exists(_))
if (allPredefFiles.nonEmpty) {
val predefClassfilesDir = new SimpleDriver().compileAndGetOutputDir(
replpp.compilerArgs(config),
inputFiles = config.predefFiles,
inputFiles = allPredefFiles,
verbose = config.verbose
).get
config.withAdditionalClasspathEntry(predefClassfilesDir)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package replpp.scripting
import replpp.{Config, allPredefFiles}

import java.nio.file.Files
import scala.util.{Failure, Success}

/**
* Main entrypoint for ScriptingDriver, i.e. it takes commandline arguments and executes a script on the current JVM.
Expand Down Expand Up @@ -43,13 +44,12 @@ object NonForkingScriptRunner {
scriptArgs = scriptArgs.toArray,
verbose = verboseEnabled
).compileAndRun() match {
case Some(exception) =>
case Success(_) => // no exception, i.e. all is good
if (verboseEnabled) System.err.println(s"script finished successfully")
case Failure(exception) =>
System.err.println(s"error during script execution: ${exception.getMessage}")
throw exception
case None => // no exception, i.e. all is good
if (verboseEnabled) System.err.println(s"script finished successfully")
}
}


}
}
47 changes: 26 additions & 21 deletions core/src/main/scala/replpp/scripting/ScriptingDriver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import java.lang.reflect.Method
import java.net.URLClassLoader
import java.nio.file.{Files, Path, Paths}
import scala.language.unsafeNulls
import scala.util.control.NonFatal
import scala.util.{Failure, Try}

/**
* Runs a given script on the current JVM.
Expand All @@ -19,9 +21,13 @@ import scala.language.unsafeNulls
* because we have a fixed class and method name that ScriptRunner uses when it embeds the script and predef code.
* */
class ScriptingDriver(compilerArgs: Array[String], predefFiles: Seq[Path], scriptFile: Path, scriptArgs: Array[String], verbose: Boolean) {
val wrappingResult = WrapForMainArgs(Files.readString(scriptFile))
val wrappedScript = Files.createTempFile("wrapped-script", ".sc")
private val wrappingResult = WrapForMainArgs(Files.readString(scriptFile))
private val wrappedScript = Files.createTempFile("wrapped-script", ".sc")
private val tempFiles = Seq.newBuilder[Path]
private var executed = false

Files.writeString(wrappedScript, wrappingResult.fullScript)
tempFiles += wrappedScript

if (verbose) {
println(s"predefFiles: ${predefFiles.mkString(";")}")
Expand All @@ -31,27 +37,26 @@ class ScriptingDriver(compilerArgs: Array[String], predefFiles: Seq[Path], scrip
println(s"compiler arguments: ${compilerArgs.mkString(",")}")
}

// TODO change return type to Try[A]?
def compileAndRun(): Option[Throwable] = {
val inputFiles = wrappedScript +: predefFiles
new SimpleDriver(lineNumberReportingAdjustment = -wrappingResult.linesBeforeWrappedCode)
.compile(compilerArgs, inputFiles, verbose) { (ctx, outDir) =>
given Context = ctx
val inheritedClasspath = ctx.settings.classpath.value
val classpathEntries = ClassPath.expandPath(inheritedClasspath, expandStar = true).map(Paths.get(_))
val mainMethod = lookupMainMethod(outDir, classpathEntries)
try {
def compileAndRun(): Try[Unit] = {
assert(!executed, "scripting driver can only be used once, and this instance has already been used.")
executed = true
val inputFiles = (wrappedScript +: predefFiles).filter(Files.exists(_))
try {
new SimpleDriver(lineNumberReportingAdjustment = -wrappingResult.linesBeforeWrappedCode)
.compile(compilerArgs, inputFiles, verbose) { (ctx, outDir) =>
given Context = ctx
tempFiles += outDir

val inheritedClasspath = ctx.settings.classpath.value
val classpathEntries = ClassPath.expandPath(inheritedClasspath, expandStar = true).map(Paths.get(_))
val mainMethod = lookupMainMethod(outDir, classpathEntries)
mainMethod.invoke(null, scriptArgs)
None // i.e. no Throwable - this is the 'good case' in the Driver api
} catch {
case e: java.lang.reflect.InvocationTargetException =>
System.err.println(s"note: we wrapped the given script in some additional code. Use --verbose to see the full script content")
Some(e.getCause)
} finally {
deleteRecursively(outDir)
Files.deleteIfExists(wrappedScript)
}
}.flatten
} catch {
case NonFatal(e) => Failure(e)
} finally {
tempFiles.result().foreach(deleteRecursively)
}
}

private def lookupMainMethod(outDir: Path, classpathEntries: Seq[Path]): Method = {
Expand Down
13 changes: 8 additions & 5 deletions core/src/main/scala/replpp/util/SimpleDriver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import replpp.scripting.CompilerError

import java.nio.file.{Files, Path}
import scala.language.unsafeNulls
import scala.util.Try

/** Compiles input files to a temporary directory
*
Expand All @@ -24,20 +25,22 @@ import scala.language.unsafeNulls
*/
class SimpleDriver(lineNumberReportingAdjustment: Int = 0) extends Driver {

def compileAndGetOutputDir[A](compilerArgs: Array[String], inputFiles: Seq[Path], verbose: Boolean): Option[Path] =
def compileAndGetOutputDir[A](compilerArgs: Array[String], inputFiles: Seq[Path], verbose: Boolean): Try[Path] =
compile(compilerArgs, inputFiles, verbose) { (ctx, outDir) => outDir }


// TODO change return type to `Try[A]?`
/** compiles given inputFiles and returns root directory that contains the class and tasty files */
def compile[A](compilerArgs: Array[String], inputFiles: Seq[Path], verbose: Boolean)(fun: (Context, Path) => A): Option[A] = {
def compile[A](compilerArgs: Array[String], inputFiles: Seq[Path], verbose: Boolean)(fun: (Context, Path) => A): Try[A] = {
if (verbose) {
println(s"compiler arguments: ${compilerArgs.mkString(",")}")
println(s"inputFiles: ${inputFiles.mkString(";")}")
}

val inputFiles0 = inputFiles.map(pathAsString).toArray
setup(compilerArgs ++ inputFiles0, initCtx.fresh).map { case (toCompile, rootCtx) =>
val allArgs = compilerArgs ++ inputFiles0
Try {
val (toCompile, rootCtx) = setup(allArgs, initCtx.fresh)
.getOrElse(throw CompilerError(s"error during setup with args=`${allArgs.mkString(" ")}`, details should have been reported already on stderr/stdout"))

val outDir = Files.createTempDirectory("scala-repl-pp")

given ctx0: Context = {
Expand Down
4 changes: 2 additions & 2 deletions server/src/test/scala/replpp/server/ReplServerTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -385,15 +385,15 @@ object Fixture {
}

def apply[T](predefCode: String = "")(urlToResult: String => T): T = {
val additionalClasspathEntryMaybe =
val additionalClasspathEntryMaybe: Option[Path] =
if (predefCode.trim.isEmpty) None
else {
val predefFile = Files.createTempFile(getClass.getName, "scala")
Files.writeString(predefFile, predefCode)
val predefClassfiles = new SimpleDriver().compileAndGetOutputDir(compilerArgs(additionalClasspathEntryMaybe = None), inputFiles = Seq(predefFile), verbose = false)

Files.delete(predefFile)
predefClassfiles
predefClassfiles.toOption
}
val embeddedRepl = new EmbeddedRepl(compilerArgs(additionalClasspathEntryMaybe))

Expand Down

0 comments on commit 64e6566

Please sign in to comment.