77
88package org .scalaexercises .evaluator
99
10- import java .io .{ByteArrayOutputStream , File }
10+ import java .io .{ByteArrayOutputStream , Closeable , File }
1111import java .math .BigInteger
12- import java .net .URLClassLoader
1312import java .security .MessageDigest
1413import java .util .jar .JarFile
1514
@@ -19,10 +18,12 @@ import coursier._
1918import coursier .cache .{ArtifactError , FileCache }
2019import coursier .util .Sync
2120import org .scalaexercises .evaluator .Eval .CompilerException
21+ import org .scalaexercises .evaluator .{Dependency => EvaluatorDependency }
2222
2323import scala .concurrent .duration ._
2424import scala .language .reflectiveCalls
2525import scala .reflect .internal .util .{AbstractFileClassLoader , BatchSourceFile , Position }
26+ import scala .reflect .internal .util .ScalaClassLoader .URLClassLoader
2627import scala .tools .nsc .io .{AbstractFile , VirtualDirectory }
2728import scala .tools .nsc .reporters ._
2829import scala .tools .nsc .{Global , Settings }
@@ -60,7 +61,9 @@ class Evaluator[F[_]: Sync](timeout: FiniteDuration = 20.seconds)(
6061
6162 val cache : FileCache [F ] = FileCache [F ].noCredentials
6263
63- def resolveArtifacts (remotes : Seq [Remote ], dependencies : Seq [Dependency ]): F [Resolution ] = {
64+ def resolveArtifacts (
65+ remotes : Seq [Remote ],
66+ dependencies : Seq [EvaluatorDependency ]): F [Resolution ] = {
6467 Resolve [F ](cache)
6568 .addDependencies(dependencies.map(dependencyToModule): _* )
6669 .addRepositories(remotes.map(remoteToRepository): _* )
@@ -70,7 +73,7 @@ class Evaluator[F[_]: Sync](timeout: FiniteDuration = 20.seconds)(
7073
7174 def fetchArtifacts (
7275 remotes : Seq [Remote ],
73- dependencies : Seq [Dependency ]): F [Either [ArtifactError , List [File ]]] =
76+ dependencies : Seq [EvaluatorDependency ]): F [Either [ArtifactError , List [File ]]] =
7477 for {
7578 resolution <- resolveArtifacts(remotes, dependencies)
7679 gatheredArtifacts <- resolution.artifacts().toList.traverse(cache.file(_).run)
@@ -109,43 +112,41 @@ class Evaluator[F[_]: Sync](timeout: FiniteDuration = 20.seconds)(
109112 }
110113 }
111114
112- private [this ] def evaluate [T ](code : String , jars : Seq [File ]): EvalResult [T ] = {
113- val eval = createEval(jars)
115+ private [this ] def evaluate [T ](code : String , jars : Seq [File ]): F [EvalResult [T ]] = {
116+ F .bracket(F .delay(createEval(jars))) { evalInstance =>
117+ val outCapture = new ByteArrayOutputStream
114118
115- val outCapture = new ByteArrayOutputStream
119+ F .delay[ EvalResult [ T ]]( Console .withOut( outCapture) {
116120
117- Console .withOut(outCapture) {
121+ val result = Try (evalInstance.execute[ T ](code, resetState = true , jars = jars))
118122
119- val result = for {
120- _ ← Try (eval.check(code))
121- result ← Try (eval.execute[T ](code, resetState = true , jars = jars))
122- } yield result
123+ val errors = evalInstance.errors
123124
124- val errors = eval.errors
125+ result match {
126+ case scala.util.Success (r) => EvalSuccess [T ](errors, r, outCapture.toString)
127+ case scala.util.Failure (t) =>
128+ t match {
129+ case e : CompilerException => CompilationError (errors)
130+ case NonFatal (e) =>
131+ EvalRuntimeError (errors, Option (RuntimeError (e, None )))
132+ case e => GeneralError (e)
133+ }
134+ }
135+ })
136+ }(EI => F .delay(EI .clean()))
125137
126- result match {
127- case scala.util.Success (r) ⇒ EvalSuccess [T ](errors, r, outCapture.toString)
128- case scala.util.Failure (t) ⇒
129- t match {
130- case e : CompilerException ⇒ CompilationError (errors)
131- case NonFatal (e) ⇒
132- EvalRuntimeError (errors, Option (RuntimeError (e, None )))
133- case e ⇒ GeneralError (e)
134- }
135- }
136- }
137138 }
138139
139140 def eval [T ](
140141 code : String ,
141142 remotes : Seq [Remote ] = Nil ,
142- dependencies : Seq [Dependency ] = Nil
143+ dependencies : Seq [EvaluatorDependency ] = Nil
143144 ): F [EvalResult [T ]] = {
144145 for {
145146 allJars <- fetchArtifacts(remotes, dependencies)
146147 result <- allJars match {
147148 case Right (jars) =>
148- timeoutTo[EvalResult [T ]](F .delay { evaluate(code, jars) } , timeout, Timeout [T ](timeout))
149+ timeoutTo[EvalResult [T ]](evaluate(code, jars), timeout, Timeout [T ](timeout))
149150 case Left (fileError) => F .pure(UnresolvedDependency [T ](fileError.describe))
150151 }
151152 } yield result
@@ -176,7 +177,7 @@ private class StringCompiler(
176177 output : AbstractFile ,
177178 settings : Settings ,
178179 messageHandler : Option [Reporter ]
179- ) {
180+ ) extends Closeable {
180181
181182 val cache = new scala.collection.mutable.HashMap [String , Class [_]]()
182183
@@ -290,6 +291,12 @@ private class StringCompiler(
290291 findClass(className, classLoader).get // fixme
291292 }
292293 }
294+
295+ override def close (): Unit = {
296+ global.cleanup
297+ global.close()
298+ reporter.reset()
299+ }
293300}
294301
295302/**
@@ -358,37 +365,32 @@ class Eval(target: Option[File] = None, jars: List[File] = Nil) {
358365 }
359366
360367 def execute [T ](className : String , code : String , resetState : Boolean , jars : Seq [File ]): T = {
361- val jarUrls = jars
362- .map(jar => new java.net.URL (s " file:// ${jar.getAbsolutePath}" ))
363- .toArray
364- val urlClassLoader =
365- new URLClassLoader (jarUrls, compiler.getClass.getClassLoader)
366- val classLoader =
367- new AbstractFileClassLoader (compilerOutputDir, urlClassLoader)
368+ val jarUrls = jars.map(jar => new java.net.URL (s " file:// ${jar.getAbsolutePath}" ))
369+ val urlClassLoader = new URLClassLoader (jarUrls, compiler.getClass.getClassLoader)
370+ val classLoader = new AbstractFileClassLoader (compilerOutputDir, urlClassLoader)
368371
369372 val cls = compiler(
370373 wrapCodeInClass(className, code),
371374 className,
372375 resetState,
373376 classLoader
374377 )
375- cls
378+
379+ val res = cls
376380 .getConstructor()
377381 .newInstance()
378382 .asInstanceOf [() => T ]
379383 .apply()
380384 .asInstanceOf [T ]
385+
386+ urlClassLoader.close()
387+
388+ res
381389 }
382390
383- /**
384- * Check if code is Eval-able.
385- * @throws CompilerException if not Eval-able.
386- */
387- def check (code : String ) = {
388- val id = uniqueId(code)
389- val className = " Evaluator__" + id
390- val wrappedCode = wrapCodeInClass(className, code)
391- compiler(wrappedCode)
391+ def clean (): Unit = {
392+ compiler.close()
393+ compilerMessageHandler.foreach(_.reset())
392394 }
393395
394396 private [this ] def uniqueId (code : String , idOpt : Option [Int ] = Some (Eval .jvmId)): String = {
0 commit comments