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

Commit c16772a

Browse files
committed
Move WasmContext.complete to Emitter.
This way, WasmContext is not responsible for emitting code anymore. It only acts as a knowledge repository.
1 parent f35e256 commit c16772a

File tree

2 files changed

+211
-191
lines changed

2 files changed

+211
-191
lines changed

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

+199-1
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
package org.scalajs.linker.backend.wasmemitter
22

33
import org.scalajs.ir.Names._
4+
import org.scalajs.ir.{Types => IRTypes}
5+
import org.scalajs.ir.{Trees => IRTrees}
6+
import org.scalajs.ir.Position
47

58
import org.scalajs.linker.interface._
9+
import org.scalajs.linker.interface.unstable._
610
import org.scalajs.linker.standard._
711
import org.scalajs.linker.standard.ModuleSet.ModuleID
812

913
import org.scalajs.linker.backend.webassembly._
14+
import org.scalajs.linker.backend.webassembly.Types._
1015

1116
import org.scalajs.logging.Logger
1217

1318
import SpecialNames._
19+
import VarGen._
1420

1521
final class Emitter(config: Emitter.Config) {
1622
import Emitter._
@@ -49,7 +55,7 @@ final class Emitter(config: Emitter.Config) {
4955
val classesWithStaticInit =
5056
sortedClasses.filter(_.hasStaticInitializer).map(_.className)
5157

52-
ctx.complete(
58+
complete(
5359
module.initializers.toList,
5460
classesWithStaticInit,
5561
module.topLevelExports
@@ -64,6 +70,198 @@ final class Emitter(config: Emitter.Config) {
6470
new Result(wasmModule, loaderContent, jsFileContent)
6571
}
6672

73+
private def complete(
74+
moduleInitializers: List[ModuleInitializer.Initializer],
75+
classesWithStaticInit: List[ClassName],
76+
topLevelExportDefs: List[LinkedTopLevelExport]
77+
)(implicit ctx: WasmContext): Unit = {
78+
/* Before generating the string globals in `genStartFunction()`, make sure
79+
* to allocate the ones that will be required by the module initializers.
80+
*/
81+
for (init <- moduleInitializers) {
82+
ModuleInitializerImpl.fromInitializer(init) match {
83+
case ModuleInitializerImpl.MainMethodWithArgs(_, _, args) =>
84+
args.foreach(ctx.addConstantStringGlobal(_))
85+
case ModuleInitializerImpl.VoidMainMethod(_, _) =>
86+
() // nothing to do
87+
}
88+
}
89+
90+
// string
91+
val (stringPool, stringPoolCount) = ctx.getFinalStringPool()
92+
ctx.moduleBuilder.addData(WasmData(genDataName.string, stringPool, WasmData.Mode.Passive))
93+
ctx.addGlobal(
94+
WasmGlobal(
95+
genGlobalName.stringLiteralCache,
96+
WasmRefType(genTypeName.anyArray),
97+
WasmExpr(
98+
List(
99+
WasmInstr.I32_CONST(stringPoolCount),
100+
WasmInstr.ARRAY_NEW_DEFAULT(genTypeName.anyArray)
101+
)
102+
),
103+
isMutable = false
104+
)
105+
)
106+
107+
genStartFunction(moduleInitializers, classesWithStaticInit, topLevelExportDefs)
108+
genDeclarativeElements()
109+
}
110+
111+
private def genStartFunction(
112+
moduleInitializers: List[ModuleInitializer.Initializer],
113+
classesWithStaticInit: List[ClassName],
114+
topLevelExportDefs: List[LinkedTopLevelExport]
115+
)(implicit ctx: WasmContext): Unit = {
116+
import WasmInstr._
117+
118+
implicit val pos = Position.NoPosition
119+
120+
implicit val fctx = WasmFunctionContext(genFunctionName.start, Nil, Nil)
121+
122+
import fctx.instrs
123+
124+
// Initialize itables
125+
for (className <- ctx.getAllClassesWithITableGlobal()) {
126+
val classInfo = ctx.getClassInfo(className)
127+
val interfaces = classInfo.ancestors.map(ctx.getClassInfo(_)).filter(_.isInterface)
128+
val resolvedMethodInfos = classInfo.resolvedMethodInfos
129+
130+
interfaces.foreach { iface =>
131+
val idx = ctx.getItableIdx(iface)
132+
instrs += WasmInstr.GLOBAL_GET(genGlobalName.forITable(className))
133+
instrs += WasmInstr.I32_CONST(idx)
134+
135+
for (method <- iface.tableEntries)
136+
instrs += ctx.refFuncWithDeclaration(resolvedMethodInfos(method).tableEntryName)
137+
instrs += WasmInstr.STRUCT_NEW(genTypeName.forITable(iface.name))
138+
instrs += WasmInstr.ARRAY_SET(genTypeName.itables)
139+
}
140+
}
141+
142+
locally {
143+
// For array classes, resolve methods in jl.Object
144+
val globalName = genGlobalName.arrayClassITable
145+
val resolvedMethodInfos = ctx.getClassInfo(ObjectClass).resolvedMethodInfos
146+
147+
for {
148+
interfaceName <- List(SerializableClass, CloneableClass)
149+
// Use getClassInfoOption in case the reachability analysis got rid of those interfaces
150+
interfaceInfo <- ctx.getClassInfoOption(interfaceName)
151+
} {
152+
instrs += GLOBAL_GET(globalName)
153+
instrs += I32_CONST(ctx.getItableIdx(interfaceInfo))
154+
155+
for (method <- interfaceInfo.tableEntries)
156+
instrs += ctx.refFuncWithDeclaration(resolvedMethodInfos(method).tableEntryName)
157+
instrs += STRUCT_NEW(genTypeName.forITable(interfaceName))
158+
instrs += ARRAY_SET(genTypeName.itables)
159+
}
160+
}
161+
162+
// Initialize the JS private field symbols
163+
164+
for (fieldName <- ctx.getAllJSPrivateFieldNames()) {
165+
instrs += WasmInstr.CALL(genFunctionName.newSymbol)
166+
instrs += WasmInstr.GLOBAL_SET(genGlobalName.forJSPrivateField(fieldName))
167+
}
168+
169+
// Emit the static initializers
170+
171+
for (className <- classesWithStaticInit) {
172+
val funcName = genFunctionName.forMethod(
173+
IRTrees.MemberNamespace.StaticConstructor,
174+
className,
175+
StaticInitializerName
176+
)
177+
instrs += WasmInstr.CALL(funcName)
178+
}
179+
180+
// Initialize the top-level exports that require it
181+
182+
for (tle <- topLevelExportDefs) {
183+
tle.tree match {
184+
case IRTrees.TopLevelJSClassExportDef(_, exportName) =>
185+
instrs += CALL(genFunctionName.loadJSClass(tle.owningClass))
186+
instrs += GLOBAL_SET(genGlobalName.forTopLevelExport(tle.exportName))
187+
case IRTrees.TopLevelModuleExportDef(_, exportName) =>
188+
instrs += CALL(genFunctionName.loadModule(tle.owningClass))
189+
instrs += GLOBAL_SET(genGlobalName.forTopLevelExport(tle.exportName))
190+
case IRTrees.TopLevelMethodExportDef(_, methodDef) =>
191+
// We only need initialization if there is a restParam
192+
if (methodDef.restParam.isDefined) {
193+
instrs += ctx.refFuncWithDeclaration(genFunctionName.forExport(tle.exportName))
194+
instrs += I32_CONST(methodDef.args.size)
195+
instrs += CALL(genFunctionName.closureRestNoData)
196+
instrs += GLOBAL_SET(genGlobalName.forTopLevelExport(tle.exportName))
197+
}
198+
case IRTrees.TopLevelFieldExportDef(_, _, _) =>
199+
// Nothing to do
200+
()
201+
}
202+
}
203+
204+
// Emit the module initializers
205+
206+
moduleInitializers.foreach { init =>
207+
def genCallStatic(className: ClassName, methodName: MethodName): Unit = {
208+
val functionName =
209+
genFunctionName.forMethod(IRTrees.MemberNamespace.PublicStatic, className, methodName)
210+
instrs += WasmInstr.CALL(functionName)
211+
}
212+
213+
val stringArrayTypeRef = IRTypes.ArrayTypeRef(IRTypes.ClassRef(BoxedStringClass), 1)
214+
215+
val callTree = ModuleInitializerImpl.fromInitializer(init) match {
216+
case ModuleInitializerImpl.MainMethodWithArgs(className, encodedMainMethodName, args) =>
217+
IRTrees.ApplyStatic(
218+
IRTrees.ApplyFlags.empty,
219+
className,
220+
IRTrees.MethodIdent(encodedMainMethodName),
221+
List(IRTrees.ArrayValue(stringArrayTypeRef, args.map(IRTrees.StringLiteral(_))))
222+
)(IRTypes.NoType)
223+
224+
case ModuleInitializerImpl.VoidMainMethod(className, encodedMainMethodName) =>
225+
IRTrees.ApplyStatic(
226+
IRTrees.ApplyFlags.empty,
227+
className,
228+
IRTrees.MethodIdent(encodedMainMethodName),
229+
Nil
230+
)(IRTypes.NoType)
231+
}
232+
233+
WasmExpressionBuilder.generateIRBody(callTree, IRTypes.NoType)
234+
}
235+
236+
// Finish the start function
237+
238+
fctx.buildAndAddToContext()
239+
ctx.moduleBuilder.setStart(genFunctionName.start)
240+
}
241+
242+
private def genDeclarativeElements()(implicit ctx: WasmContext): Unit = {
243+
// Aggregated Elements
244+
245+
val funcDeclarations = ctx.getAllFuncDeclarations()
246+
247+
if (funcDeclarations.nonEmpty) {
248+
/* Functions that are referred to with `ref.func` in the Code section
249+
* must be declared ahead of time in one of the earlier sections
250+
* (otherwise the module does not validate). It can be the Global section
251+
* if they are meaningful there (which is why `ref.func` in the vtables
252+
* work out of the box). In the absence of any other specific place, an
253+
* Element section with the declarative mode is the recommended way to
254+
* introduce these declarations.
255+
*/
256+
val exprs = funcDeclarations.map { name =>
257+
WasmExpr(List(WasmInstr.REF_FUNC(name)))
258+
}
259+
ctx.moduleBuilder.addElement(
260+
WasmElement(WasmRefType.funcref, exprs, WasmElement.Mode.Declarative)
261+
)
262+
}
263+
}
264+
67265
private def buildJSFileContent(
68266
module: ModuleSet.Module,
69267
wasmFileName: String,

0 commit comments

Comments
 (0)