From bd230a9749da4969cff5f22f431ed1132fc6c87f Mon Sep 17 00:00:00 2001 From: Tobias Schlatter Date: Wed, 16 Sep 2020 09:54:31 +0200 Subject: [PATCH] Support 0 modules with GCC --- .../closure/ClosureLinkerBackend.scala | 48 +++++++++++-------- .../org/scalajs/linker/GCCLinkerTest.scala | 33 +++++++++++++ .../scala/org/scalajs/linker/LinkerTest.scala | 2 +- .../linker/testutils/LinkingUtils.scala | 3 +- 4 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 linker/jvm/src/test/scala/org/scalajs/linker/GCCLinkerTest.scala diff --git a/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureLinkerBackend.scala b/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureLinkerBackend.scala index fad398165f..bae3acd149 100644 --- a/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureLinkerBackend.scala +++ b/linker/jvm/src/main/scala/org/scalajs/linker/backend/closure/ClosureLinkerBackend.scala @@ -76,35 +76,38 @@ final class ClosureLinkerBackend(config: LinkerBackendImpl.Config) implicit ec: ExecutionContext): Future[Report] = { verifyModuleSet(moduleSet) - require(moduleSet.modules.size == 1, + require(moduleSet.modules.size <= 1, "Cannot use multiple modules with the Closure Compiler") - val sjsModule = moduleSet.modules.head - + // Run Emitter even with 0 modules, to keep its internal state consistent. val emitterResult = logger.time("Emitter") { emitter.emit(moduleSet, logger) } - val closureModule = logger.time("Closure: Create trees)") { - buildModule(emitterResult.body(sjsModule.id)) - } + val gccResult = for { + sjsModule <- moduleSet.modules.headOption + } yield { + val closureModule = logger.time("Closure: Create trees)") { + buildModule(emitterResult.body(sjsModule.id)) + } - val (code, sourceMap) = logger.time("Closure: Compiler pass") { - val options = closureOptions(sjsModule.id) + logger.time("Closure: Compiler pass") { + val options = closureOptions(sjsModule.id) - val externs = java.util.Arrays.asList( - ClosureSource.fromCode("ScalaJSExterns.js", - ClosureLinkerBackend.ScalaJSExterns), - ClosureSource.fromCode("ScalaJSGlobalRefs.js", - makeExternsForGlobalRefs(emitterResult.globalRefs)), - ClosureSource.fromCode("ScalaJSExportExterns.js", - makeExternsForExports(emitterResult.topLevelVarDecls, sjsModule))) + val externs = java.util.Arrays.asList( + ClosureSource.fromCode("ScalaJSExterns.js", + ClosureLinkerBackend.ScalaJSExterns), + ClosureSource.fromCode("ScalaJSGlobalRefs.js", + makeExternsForGlobalRefs(emitterResult.globalRefs)), + ClosureSource.fromCode("ScalaJSExportExterns.js", + makeExternsForExports(emitterResult.topLevelVarDecls, sjsModule))) - compile(externs, closureModule, options, logger) + compile(externs, closureModule, options, logger) + } } logger.timeFuture("Closure: Write result") { - writeResult(moduleSet, emitterResult.header, code, emitterResult.footer, sourceMap, output) + writeResult(moduleSet, emitterResult.header, emitterResult.footer, gccResult, output) } } @@ -175,14 +178,18 @@ final class ClosureLinkerBackend(config: LinkerBackendImpl.Config) content.toString() } - private def writeResult(moduleSet: ModuleSet, header: String, body: String, - footer: String, sourceMap: SourceMap, output: OutputDirectory)( + private def writeResult(moduleSet: ModuleSet, header: String, footer: String, + gccResult: Option[(String, SourceMap)], output: OutputDirectory)( implicit ec: ExecutionContext): Future[Report] = { + /* `gccResult` is an Option, because we might have no module at all. + * We call `.get` in the write methods to fail if we get a called anyways. + */ val writer = new OutputWriter(output, config) { private def writeCode(writer: Writer): Unit = { + val code = gccResult.get._1 writer.write(header) - writer.write(body) + writer.write(code) writer.write(footer) } @@ -198,6 +205,7 @@ final class ClosureLinkerBackend(config: LinkerBackendImpl.Config) writeCode(jsFileWriter) jsFileWriter.write("//# sourceMappingURL=" + sourceMapURI + "\n") + val sourceMap = gccResult.get._2 sourceMap.setWrapperPrefix(header) sourceMap.appendTo(sourceMapWriter, jsFileURI) } diff --git a/linker/jvm/src/test/scala/org/scalajs/linker/GCCLinkerTest.scala b/linker/jvm/src/test/scala/org/scalajs/linker/GCCLinkerTest.scala new file mode 100644 index 0000000000..a399e4588b --- /dev/null +++ b/linker/jvm/src/test/scala/org/scalajs/linker/GCCLinkerTest.scala @@ -0,0 +1,33 @@ +/* + * Scala.js (https://www.scala-js.org/) + * + * Copyright EPFL. + * + * Licensed under Apache License 2.0 + * (https://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package org.scalajs.linker + +import org.junit.Test + +import org.scalajs.junit.async._ + +import org.scalajs.linker.interface.StandardConfig + +import org.scalajs.linker.testutils.LinkingUtils._ + +class GCCLinkerTest { + import scala.concurrent.ExecutionContext.Implicits.global + + @Test + def linkEmpty(): AsyncResult = await { + /* Check a degenerate case where there are not public modules at all. + * See the special check on ModuleSplitter for details. + */ + testLink(Nil, Nil, config = StandardConfig().withClosureCompiler(true)) + } +} diff --git a/linker/shared/src/test/scala/org/scalajs/linker/LinkerTest.scala b/linker/shared/src/test/scala/org/scalajs/linker/LinkerTest.scala index 809e03b737..18182c1ca0 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/LinkerTest.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/LinkerTest.scala @@ -69,7 +69,7 @@ class LinkerTest { _ <- OutputDirectoryImpl.fromOutputDirectory(outputDirectory) .writeFull(staleFileName, ByteBuffer.wrap(Array())) report <- testLink(helloWorldClassDefs, MainTestModuleInitializers, - outputDirectory) + output = outputDirectory) } yield { assertFalse(outputDirectory.content(staleFileName).isDefined) assertTrue(outputDirectory.content(report.publicModules.head.jsFileName).isDefined) diff --git a/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala b/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala index e0fbd80758..0c0fb6928f 100644 --- a/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala +++ b/linker/shared/src/test/scala/org/scalajs/linker/testutils/LinkingUtils.scala @@ -24,10 +24,11 @@ import org.scalajs.linker.interface._ object LinkingUtils { def testLink(classDefs: Seq[ClassDef], moduleInitializers: List[ModuleInitializer], + config: StandardConfig = StandardConfig(), output: OutputDirectory = MemOutputDirectory())( implicit ec: ExecutionContext): Future[Report] = { - val linker = StandardImpl.linker(StandardConfig()) + val linker = StandardImpl.linker(config) val classDefsFiles = classDefs.map(MemClassDefIRFile(_)) TestIRRepo.minilib.flatMap { stdLibFiles =>