Skip to content

Commit

Permalink
Support 0 modules with GCC
Browse files Browse the repository at this point in the history
  • Loading branch information
gzm0 committed Sep 16, 2020
1 parent 8efd091 commit bd230a9
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 22 deletions.
Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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)
}

Expand All @@ -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)
}
Expand Down
33 changes: 33 additions & 0 deletions 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))
}
}
Expand Up @@ -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)
Expand Down
Expand Up @@ -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 =>
Expand Down

0 comments on commit bd230a9

Please sign in to comment.