Skip to content
This repository was archived by the owner on Jul 12, 2024. It is now read-only.

Commit c9b8270

Browse files
authored
Merge pull request #123 from sjrd/reorg-emitter
Reorganize the emitter to better match the JS backend.
2 parents 878e8cd + c16772a commit c9b8270

File tree

6 files changed

+1002
-832
lines changed

6 files changed

+1002
-832
lines changed

Diff for: wasm/src/main/scala/org/scalajs/linker/backend/WebAssemblyLinkerBackend.scala

+12-106
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import scala.concurrent.{ExecutionContext, Future}
55
import java.nio.ByteBuffer
66
import java.nio.charset.StandardCharsets
77

8-
import org.scalajs.ir.Names._
9-
108
import org.scalajs.logging.Logger
119

1210
import org.scalajs.linker._
@@ -16,8 +14,7 @@ import org.scalajs.linker.standard._
1614

1715
import org.scalajs.linker.backend.webassembly._
1816

19-
import org.scalajs.linker.backend.wasmemitter._
20-
import org.scalajs.linker.backend.wasmemitter.SpecialNames._
17+
import org.scalajs.linker.backend.wasmemitter.Emitter
2118

2219
final class WebAssemblyLinkerBackend(
2320
linkerConfig: StandardConfig,
@@ -32,38 +29,18 @@ final class WebAssemblyLinkerBackend(
3229
"The WebAssembly backend does not support the optimizer yet."
3330
)
3431

35-
/* The symbol requirements of our back-end.
36-
* The symbol requirements tell the LinkerFrontend that we need these
37-
* symbols to always be reachable, even if no "user-land" IR requires them.
38-
* They are roots for the reachability analysis, together with module
39-
* initializers and top-level exports.
40-
* If we don't do this, the linker frontend will dead-code eliminate our
41-
* box classes.
42-
*/
43-
val symbolRequirements: SymbolRequirement = {
44-
val factory = SymbolRequirement.factory("wasm")
45-
46-
factory.multiple(
47-
factory.instantiateClass(ClassClass, ClassCtor),
48-
factory.instantiateClass(CharBoxClass, CharBoxCtor),
49-
factory.instantiateClass(LongBoxClass, LongBoxCtor),
50-
51-
// See genIdentityHashCode in HelperFunctions
52-
factory.callMethodStatically(BoxedDoubleClass, hashCodeMethodName),
53-
factory.callMethodStatically(BoxedStringClass, hashCodeMethodName)
54-
)
55-
}
32+
val loaderJSFileName = OutputPatternsImpl.jsFile(linkerConfig.outputPatterns, "__loader")
5633

57-
// Our injected IR files are handled by WebAssemblyStandardLinkerImpl instead
58-
def injectedIRFiles: Seq[IRFile] = Nil
34+
private val emitter: Emitter =
35+
new Emitter(Emitter.Config(coreSpec, loaderJSFileName))
36+
37+
val symbolRequirements: SymbolRequirement = emitter.symbolRequirements
38+
39+
def injectedIRFiles: Seq[IRFile] = emitter.injectedIRFiles
5940

6041
def emit(moduleSet: ModuleSet, output: OutputDirectory, logger: Logger)(implicit
6142
ec: ExecutionContext
6243
): Future[Report] = {
63-
64-
val builder = new WasmBuilder(coreSpec)
65-
implicit val context: WasmContext = new WasmContext()
66-
6744
val onlyModule = moduleSet.modules match {
6845
case onlyModule :: Nil =>
6946
onlyModule
@@ -75,47 +52,15 @@ final class WebAssemblyLinkerBackend(
7552
}
7653
val moduleID = onlyModule.id.id
7754

78-
/* Sort by ancestor count so that superclasses always appear before
79-
* subclasses, then tie-break by name for stability.
80-
*/
81-
val sortedClasses = onlyModule.classDefs.sortWith { (a, b) =>
82-
val cmp = Integer.compare(a.ancestors.size, b.ancestors.size)
83-
if (cmp != 0) cmp < 0
84-
else a.className.compareTo(b.className) < 0
85-
}
86-
87-
// sortedClasses.foreach(cls => println(utils.LinkedClassPrinters.showLinkedClass(cls)))
88-
89-
Preprocessor.preprocess(sortedClasses, onlyModule.topLevelExports)(context)
90-
HelperFunctions.genGlobalHelpers()
91-
builder.genPrimitiveTypeDataGlobals()
92-
sortedClasses.foreach { clazz =>
93-
builder.transformClassDef(clazz)
94-
}
95-
// Array classes extend j.l.Object, so they must come after transformClassDef's
96-
builder.genArrayClasses()
97-
onlyModule.topLevelExports.foreach { tle =>
98-
builder.transformTopLevelExport(tle)
99-
}
100-
101-
val classesWithStaticInit =
102-
sortedClasses.filter(_.hasStaticInitializer).map(_.className)
103-
104-
context.complete(
105-
onlyModule.initializers.toList,
106-
classesWithStaticInit,
107-
onlyModule.topLevelExports
108-
)
109-
110-
val wasmModule = context.moduleBuilder.build()
55+
val emitterResult = emitter.emit(onlyModule, logger)
56+
val wasmModule = emitterResult.wasmModule
11157

11258
val outputImpl = OutputDirectoryImpl.fromOutputDirectory(output)
11359

11460
val watFileName = s"$moduleID.wat"
11561
val wasmFileName = s"$moduleID.wasm"
11662
val sourceMapFileName = s"$wasmFileName.map"
11763
val jsFileName = OutputPatternsImpl.jsFile(linkerConfig.outputPatterns, moduleID)
118-
val loaderJSFileName = OutputPatternsImpl.jsFile(linkerConfig.outputPatterns, "__loader")
11964

12065
val filesToProduce0 = Set(
12166
wasmFileName,
@@ -168,12 +113,10 @@ final class WebAssemblyLinkerBackend(
168113
}
169114

170115
def writeLoaderFile(): Future[Unit] =
171-
outputImpl.writeFull(loaderJSFileName, ByteBuffer.wrap(LoaderContent.bytesContent))
116+
outputImpl.writeFull(loaderJSFileName, ByteBuffer.wrap(emitterResult.loaderContent))
172117

173118
def writeJSFile(): Future[Unit] = {
174-
val jsFileOutput =
175-
buildJSFileOutput(onlyModule, loaderJSFileName, wasmFileName, context.allImportedModules)
176-
val jsFileOutputBytes = jsFileOutput.getBytes(StandardCharsets.UTF_8)
119+
val jsFileOutputBytes = emitterResult.jsFileContent.getBytes(StandardCharsets.UTF_8)
177120
outputImpl.writeFull(jsFileName, ByteBuffer.wrap(jsFileOutputBytes))
178121
}
179122

@@ -194,41 +137,4 @@ final class WebAssemblyLinkerBackend(
194137
new ReportImpl(List(reportModule))
195138
}
196139
}
197-
198-
private def buildJSFileOutput(
199-
module: ModuleSet.Module,
200-
loaderJSFileName: String,
201-
wasmFileName: String,
202-
importedModules: List[String]
203-
): String = {
204-
val (moduleImports, importedModulesItems) = (for {
205-
(moduleName, idx) <- importedModules.zipWithIndex
206-
} yield {
207-
val identName = s"imported$idx"
208-
val escapedModuleName = "\"" + moduleName + "\""
209-
val moduleImport = s"import * as $identName from $escapedModuleName"
210-
val item = s" $escapedModuleName: $identName,"
211-
(moduleImport, item)
212-
}).unzip
213-
214-
/* TODO This is not correct for exported *vars*, since they won't receive
215-
* updates from mutations after loading.
216-
*/
217-
val reExportStats = for {
218-
exportName <- module.topLevelExports.map(_.exportName)
219-
} yield {
220-
s"export let $exportName = __exports.$exportName;"
221-
}
222-
223-
s"""
224-
|${moduleImports.mkString("\n")}
225-
|
226-
|import { load as __load } from './${loaderJSFileName}';
227-
|const __exports = await __load('./${wasmFileName}', {
228-
|${importedModulesItems.mkString("\n")}
229-
|});
230-
|
231-
|${reExportStats.mkString("\n")}
232-
""".stripMargin.trim() + "\n"
233-
}
234140
}

0 commit comments

Comments
 (0)