Skip to content

Commit 5801e1a

Browse files
committed
Fix #3589: Use java.nio.file.Path instead of VirtualBinaryFile
1 parent 032f62f commit 5801e1a

File tree

5 files changed

+72
-116
lines changed

5 files changed

+72
-116
lines changed

js-envs-test-kit/src/main/scala/org/scalajs/jsenv/test/RunTests.scala

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212

1313
package org.scalajs.jsenv.test
1414

15+
import com.google.common.jimfs.Jimfs
16+
1517
import org.junit.Assume._
1618
import org.junit.{Test, Before, AssumptionViolatedException}
1719

18-
import org.scalajs.io.VirtualBinaryFile
1920
import org.scalajs.jsenv._
2021
import org.scalajs.jsenv.test.kit.{TestKit, Run}
2122

@@ -133,13 +134,7 @@ private[test] class RunTests(config: JSEnvSuiteConfig, withCom: Boolean) {
133134

134135
@Test
135136
def noThrowOnBadFileTest: Unit = {
136-
def fail() = throw new java.io.IOException("exception for test")
137-
138-
val badFile = new VirtualBinaryFile {
139-
def path: String = fail()
140-
def exists: Boolean = fail()
141-
def inputStream: java.io.InputStream = fail()
142-
}
137+
val badFile = Jimfs.newFileSystem().getPath("nonexistent")
143138

144139
// `start` may not throw but must fail asynchronously
145140
withRun(Input.ScriptsToLoad(badFile :: Nil)) {

js-envs-test-kit/src/main/scala/org/scalajs/jsenv/test/kit/TestKit.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ import scala.concurrent.ExecutionContext
1616
import scala.concurrent.duration.FiniteDuration
1717

1818
import java.io.InputStream
19+
import java.nio.charset.StandardCharsets
20+
import java.nio.file._
1921
import java.util.concurrent.Executors
2022

21-
import org.scalajs.io.MemVirtualBinaryFile
23+
import com.google.common.jimfs.Jimfs
24+
2225
import org.scalajs.jsenv._
2326

2427
/** TestKit is a utility class to simplify testing of [[JSEnv]]s.
@@ -152,7 +155,9 @@ private object TestKit {
152155
ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor())
153156

154157
private def codeToInput(code: String): Input = {
155-
val vf = MemVirtualBinaryFile.fromStringUTF8("testScript.js", code)
156-
Input.ScriptsToLoad(List(vf))
158+
val p = Files.write(
159+
Jimfs.newFileSystem().getPath("testScript.js"),
160+
code.getBytes(StandardCharsets.UTF_8))
161+
Input.ScriptsToLoad(List(p))
157162
}
158163
}

js-envs/src/main/scala/org/scalajs/jsenv/Input.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
package org.scalajs.jsenv
1414

15-
import org.scalajs.io._
15+
import java.nio.file.Path
1616

1717
/** Input to a [[JSEnv]].
1818
*
@@ -27,7 +27,7 @@ abstract class Input private ()
2727

2828
object Input {
2929
/** All files are to be loaded as scripts into the global scope in the order given. */
30-
final case class ScriptsToLoad(scripts: List[VirtualBinaryFile]) extends Input
30+
final case class ScriptsToLoad(scripts: List[Path]) extends Input
3131

3232
/** All files are to be loaded as ES modules, in the given order.
3333
*
@@ -36,11 +36,11 @@ object Input {
3636
* `ESModulesToLoad` input if the `modules` argument has more than one
3737
* element.
3838
*/
39-
final case class ESModulesToLoad(modules: List[VirtualBinaryFile])
39+
final case class ESModulesToLoad(modules: List[Path])
4040
extends Input
4141

4242
/** All files are to be loaded as CommonJS modules, in the given order. */
43-
final case class CommonJSModulesToLoad(modules: List[VirtualBinaryFile])
43+
final case class CommonJSModulesToLoad(modules: List[Path])
4444
extends Input
4545
}
4646

nodejs-env/src/main/scala/org/scalajs/jsenv/nodejs/ComSupport.scala

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@
1212

1313
package org.scalajs.jsenv.nodejs
1414

15-
import java.io._
16-
import java.net._
17-
18-
import org.scalajs.io.{VirtualBinaryFile, MemVirtualBinaryFile}
19-
import org.scalajs.jsenv._
20-
2115
import scala.collection.immutable
2216
import scala.concurrent._
2317
import scala.util.{Failure, Success}
@@ -26,6 +20,15 @@ import scala.util.control.NonFatal
2620
// TODO Replace this by a better execution context on the RunConfig.
2721
import scala.concurrent.ExecutionContext.Implicits.global
2822

23+
import java.io._
24+
import java.net._
25+
import java.nio.charset.StandardCharsets
26+
import java.nio.file._
27+
28+
import com.google.common.jimfs.Jimfs
29+
30+
import org.scalajs.jsenv._
31+
2932
private final class ComRun(run: JSRun, handleMessage: String => Unit,
3033
serverSocket: ServerSocket) extends JSComRun {
3134
import ComRun._
@@ -200,11 +203,10 @@ object ComRun {
200203
* @param config Configuration for the run.
201204
* @param onMessage callback upon message reception.
202205
* @param startRun [[JSRun]] launcher. Gets passed a
203-
* [[org.scalajs.io.VirtualBinaryFile VirtualBinaryFile]] that
204-
* initializes `scalaJSCom` on `global`. Requires Node.js libraries.
206+
* [[java.nio.file.Path Path]] that initializes `scalaJSCom` on
207+
* `global`. Requires Node.js libraries.
205208
*/
206-
def start(config: RunConfig, onMessage: String => Unit)(
207-
startRun: VirtualBinaryFile => JSRun): JSComRun = {
209+
def start(config: RunConfig, onMessage: String => Unit)(startRun: Path => JSRun): JSComRun = {
208210
try {
209211
val serverSocket =
210212
new ServerSocket(0, 0, InetAddress.getByName(null)) // Loopback address
@@ -240,8 +242,9 @@ object ComRun {
240242
s.writeChars(msg)
241243
}
242244

243-
private def setupFile(port: Int): VirtualBinaryFile = {
244-
MemVirtualBinaryFile.fromStringUTF8("comSetup.js",
245+
private def setupFile(port: Int): Path = {
246+
Files.write(
247+
Jimfs.newFileSystem().getPath("comSetup.js"),
245248
s"""
246249
|(function() {
247250
| // The socket for communication
@@ -303,6 +306,6 @@ object ComRun {
303306
| }
304307
| }
305308
|}).call(this);
306-
""".stripMargin)
309+
""".stripMargin.getBytes(StandardCharsets.UTF_8))
307310
}
308311
}

nodejs-env/src/main/scala/org/scalajs/jsenv/nodejs/NodeJSEnv.scala

Lines changed: 41 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,19 @@
1212

1313
package org.scalajs.jsenv.nodejs
1414

15-
import java.nio.charset.StandardCharsets
1615

1716
import scala.annotation.tailrec
18-
import scala.collection.JavaConverters._
17+
18+
import java.io._
19+
import java.nio.charset.StandardCharsets
20+
import java.nio.file._
1921

2022
import org.scalajs.jsenv._
2123
import org.scalajs.jsenv.JSUtils.escapeJS
2224

23-
import org.scalajs.io._
2425
import org.scalajs.logging._
2526

26-
import java.io._
27+
import com.google.common.jimfs.Jimfs
2728

2829
final class NodeJSEnv(config: NodeJSEnv.Config) extends JSEnv {
2930
import NodeJSEnv._
@@ -57,8 +58,7 @@ final class NodeJSEnv(config: NodeJSEnv.Config) extends JSEnv {
5758
}
5859
}
5960

60-
private def internalStart(initFiles: List[VirtualBinaryFile], input: Input,
61-
runConfig: RunConfig): JSRun = {
61+
private def internalStart(initFiles: List[Path], input: Input, runConfig: RunConfig): JSRun = {
6262
val command = config.executable :: config.args
6363
val externalConfig = ExternalJSRun.Config()
6464
.withEnv(env)
@@ -67,7 +67,7 @@ final class NodeJSEnv(config: NodeJSEnv.Config) extends JSEnv {
6767
NodeJSEnv.write(initFiles, input))
6868
}
6969

70-
private def initFiles: List[VirtualBinaryFile] = config.sourceMap match {
70+
private def initFiles: List[Path] = config.sourceMap match {
7171
case SourceMap.Disable => Nil
7272
case SourceMap.EnableIfAvailable => installSourceMapIfAvailable :: Nil
7373
case SourceMap.Enable => installSourceMap :: Nil
@@ -83,43 +83,45 @@ final class NodeJSEnv(config: NodeJSEnv.Config) extends JSEnv {
8383
}
8484

8585
object NodeJSEnv {
86+
private lazy val fs = Jimfs.newFileSystem()
87+
8688
private lazy val validator = ExternalJSRun.supports(RunConfig.Validator())
8789

8890
private lazy val installSourceMapIfAvailable = {
89-
MemVirtualBinaryFile.fromStringUTF8("sourceMapSupport.js",
91+
Files.write(
92+
fs.getPath("optionalSourceMapSupport.js"),
9093
"""
9194
|try {
9295
| require('source-map-support').install();
9396
|} catch (e) {
9497
|};
95-
""".stripMargin
96-
)
98+
""".stripMargin.getBytes(StandardCharsets.UTF_8))
9799
}
98100

99101
private lazy val installSourceMap = {
100-
MemVirtualBinaryFile.fromStringUTF8("sourceMapSupport.js",
101-
"require('source-map-support').install();")
102+
Files.write(
103+
fs.getPath("sourceMapSupport.js"),
104+
"require('source-map-support').install();".getBytes(StandardCharsets.UTF_8))
102105
}
103106

104-
private def write(initFiles: List[VirtualBinaryFile], input: Input)(
105-
out: OutputStream): Unit = {
107+
private def write(initFiles: List[Path], input: Input)(out: OutputStream): Unit = {
106108
val p = new PrintStream(out, false, "UTF8")
107109
try {
108-
def writeRunScript(file: VirtualBinaryFile): Unit = {
109-
file match {
110-
case file: FileVirtualBinaryFile =>
111-
val pathJS = "\"" + escapeJS(file.file.getAbsolutePath) + "\""
112-
p.println(s"""
113-
require('vm').runInThisContext(
114-
require('fs').readFileSync($pathJS, { encoding: "utf-8" }),
115-
{ filename: $pathJS, displayErrors: true }
116-
);
117-
""")
118-
119-
case _ =>
120-
val code = readInputStreamToString(file.inputStream)
110+
def writeRunScript(path: Path): Unit = {
111+
try {
112+
val f = path.toFile
113+
val pathJS = "\"" + escapeJS(f.getAbsolutePath) + "\""
114+
p.println(s"""
115+
require('vm').runInThisContext(
116+
require('fs').readFileSync($pathJS, { encoding: "utf-8" }),
117+
{ filename: $pathJS, displayErrors: true }
118+
);
119+
""")
120+
} catch {
121+
case _: UnsupportedOperationException =>
122+
val code = new String(Files.readAllBytes(path), StandardCharsets.UTF_8)
121123
val codeJS = "\"" + escapeJS(code) + "\""
122-
val pathJS = "\"" + escapeJS(file.path) + "\""
124+
val pathJS = "\"" + escapeJS(path.toString) + "\""
123125
p.println(s"""
124126
require('vm').runInThisContext(
125127
$codeJS,
@@ -129,17 +131,6 @@ object NodeJSEnv {
129131
}
130132
}
131133

132-
def writeRequire(file: VirtualBinaryFile): Unit = {
133-
file match {
134-
case file: FileVirtualBinaryFile =>
135-
p.println(s"""require("${escapeJS(file.file.getAbsolutePath)}")""")
136-
137-
case _ =>
138-
val f = tmpFile(file.path, file.inputStream)
139-
p.println(s"""require("${escapeJS(f.getAbsolutePath)}")""")
140-
}
141-
}
142-
143134
for (initFile <- initFiles)
144135
writeRunScript(initFile)
145136

@@ -150,16 +141,11 @@ object NodeJSEnv {
150141

151142
case Input.CommonJSModulesToLoad(modules) =>
152143
for (module <- modules)
153-
writeRequire(module)
144+
p.println(s"""require("${escapeJS(toFile(module).getAbsolutePath)}")""")
154145

155146
case Input.ESModulesToLoad(modules) =>
156147
if (modules.nonEmpty) {
157-
val uris = modules.map {
158-
case module: FileVirtualBinaryFile =>
159-
module.file.toURI
160-
case module =>
161-
tmpFile(module.path, module.inputStream).toURI
162-
}
148+
val uris = modules.map(m => toFile(m).toURI)
163149

164150
val imports = uris.map { uri =>
165151
s"""import("${escapeJS(uri.toASCIIString)}")"""
@@ -176,7 +162,8 @@ object NodeJSEnv {
176162
|});
177163
""".stripMargin
178164
}
179-
val f = tmpFile("importer.js", importerFileContent)
165+
val f = createTmpFile("importer.js")
166+
Files.write(f.toPath, importerFileContent.getBytes(StandardCharsets.UTF_8))
180167
p.println(s"""require("${escapeJS(f.getAbsolutePath)}");""")
181168
}
182169
}
@@ -185,48 +172,14 @@ object NodeJSEnv {
185172
}
186173
}
187174

188-
private def readInputStreamToString(inputStream: InputStream): String = {
189-
val baos = new java.io.ByteArrayOutputStream
190-
val in = inputStream
175+
private def toFile(path: Path): File = {
191176
try {
192-
val buf = new Array[Byte](4096)
193-
194-
@tailrec
195-
def loop(): Unit = {
196-
val read = in.read(buf)
197-
if (read != -1) {
198-
baos.write(buf, 0, read)
199-
loop()
200-
}
201-
}
202-
203-
loop()
204-
} finally {
205-
in.close()
206-
}
207-
new String(baos.toByteArray(), StandardCharsets.UTF_8)
208-
}
209-
210-
private def tmpFile(path: String, content: String): File = {
211-
import java.nio.file.{Files, StandardOpenOption}
212-
213-
val f = createTmpFile(path)
214-
val contentList = new java.util.ArrayList[String]()
215-
contentList.add(content)
216-
Files.write(f.toPath(), contentList, StandardCharsets.UTF_8,
217-
StandardOpenOption.TRUNCATE_EXISTING)
218-
f
219-
}
220-
221-
private def tmpFile(path: String, content: InputStream): File = {
222-
import java.nio.file.{Files, StandardCopyOption}
223-
224-
try {
225-
val f = createTmpFile(path)
226-
Files.copy(content, f.toPath(), StandardCopyOption.REPLACE_EXISTING)
227-
f
228-
} finally {
229-
content.close()
177+
path.toFile
178+
} catch {
179+
case _: UnsupportedOperationException =>
180+
val f = createTmpFile(path.toString)
181+
Files.copy(path, f.toPath(), StandardCopyOption.REPLACE_EXISTING)
182+
f
230183
}
231184
}
232185

0 commit comments

Comments
 (0)