7
7
8
8
package org .scalaexercises .evaluator
9
9
10
- import java .io .{ByteArrayOutputStream , File }
10
+ import java .io .{ByteArrayOutputStream , Closeable , File }
11
11
import java .math .BigInteger
12
- import java .net .URLClassLoader
13
12
import java .security .MessageDigest
14
13
import java .util .jar .JarFile
15
14
@@ -19,10 +18,12 @@ import coursier._
19
18
import coursier .cache .{ArtifactError , FileCache }
20
19
import coursier .util .Sync
21
20
import org .scalaexercises .evaluator .Eval .CompilerException
21
+ import org .scalaexercises .evaluator .{Dependency => EvaluatorDependency }
22
22
23
23
import scala .concurrent .duration ._
24
24
import scala .language .reflectiveCalls
25
25
import scala .reflect .internal .util .{AbstractFileClassLoader , BatchSourceFile , Position }
26
+ import scala .reflect .internal .util .ScalaClassLoader .URLClassLoader
26
27
import scala .tools .nsc .io .{AbstractFile , VirtualDirectory }
27
28
import scala .tools .nsc .reporters ._
28
29
import scala .tools .nsc .{Global , Settings }
@@ -60,7 +61,9 @@ class Evaluator[F[_]: Sync](timeout: FiniteDuration = 20.seconds)(
60
61
61
62
val cache : FileCache [F ] = FileCache [F ].noCredentials
62
63
63
- def resolveArtifacts (remotes : Seq [Remote ], dependencies : Seq [Dependency ]): F [Resolution ] = {
64
+ def resolveArtifacts (
65
+ remotes : Seq [Remote ],
66
+ dependencies : Seq [EvaluatorDependency ]): F [Resolution ] = {
64
67
Resolve [F ](cache)
65
68
.addDependencies(dependencies.map(dependencyToModule): _* )
66
69
.addRepositories(remotes.map(remoteToRepository): _* )
@@ -70,7 +73,7 @@ class Evaluator[F[_]: Sync](timeout: FiniteDuration = 20.seconds)(
70
73
71
74
def fetchArtifacts (
72
75
remotes : Seq [Remote ],
73
- dependencies : Seq [Dependency ]): F [Either [ArtifactError , List [File ]]] =
76
+ dependencies : Seq [EvaluatorDependency ]): F [Either [ArtifactError , List [File ]]] =
74
77
for {
75
78
resolution <- resolveArtifacts(remotes, dependencies)
76
79
gatheredArtifacts <- resolution.artifacts().toList.traverse(cache.file(_).run)
@@ -109,43 +112,41 @@ class Evaluator[F[_]: Sync](timeout: FiniteDuration = 20.seconds)(
109
112
}
110
113
}
111
114
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
114
118
115
- val outCapture = new ByteArrayOutputStream
119
+ F .delay[ EvalResult [ T ]]( Console .withOut( outCapture) {
116
120
117
- Console .withOut(outCapture) {
121
+ val result = Try (evalInstance.execute[ T ](code, resetState = true , jars = jars))
118
122
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
123
124
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()))
125
137
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
- }
137
138
}
138
139
139
140
def eval [T ](
140
141
code : String ,
141
142
remotes : Seq [Remote ] = Nil ,
142
- dependencies : Seq [Dependency ] = Nil
143
+ dependencies : Seq [EvaluatorDependency ] = Nil
143
144
): F [EvalResult [T ]] = {
144
145
for {
145
146
allJars <- fetchArtifacts(remotes, dependencies)
146
147
result <- allJars match {
147
148
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))
149
150
case Left (fileError) => F .pure(UnresolvedDependency [T ](fileError.describe))
150
151
}
151
152
} yield result
@@ -176,7 +177,7 @@ private class StringCompiler(
176
177
output : AbstractFile ,
177
178
settings : Settings ,
178
179
messageHandler : Option [Reporter ]
179
- ) {
180
+ ) extends Closeable {
180
181
181
182
val cache = new scala.collection.mutable.HashMap [String , Class [_]]()
182
183
@@ -290,6 +291,12 @@ private class StringCompiler(
290
291
findClass(className, classLoader).get // fixme
291
292
}
292
293
}
294
+
295
+ override def close (): Unit = {
296
+ global.cleanup
297
+ global.close()
298
+ reporter.reset()
299
+ }
293
300
}
294
301
295
302
/**
@@ -358,37 +365,32 @@ class Eval(target: Option[File] = None, jars: List[File] = Nil) {
358
365
}
359
366
360
367
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)
368
371
369
372
val cls = compiler(
370
373
wrapCodeInClass(className, code),
371
374
className,
372
375
resetState,
373
376
classLoader
374
377
)
375
- cls
378
+
379
+ val res = cls
376
380
.getConstructor()
377
381
.newInstance()
378
382
.asInstanceOf [() => T ]
379
383
.apply()
380
384
.asInstanceOf [T ]
385
+
386
+ urlClassLoader.close()
387
+
388
+ res
381
389
}
382
390
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())
392
394
}
393
395
394
396
private [this ] def uniqueId (code : String , idOpt : Option [Int ] = Some (Eval .jvmId)): String = {
0 commit comments