1
1
package org .scalajs .linker .backend .wasmemitter
2
2
3
3
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
4
7
5
8
import org .scalajs .linker .interface ._
9
+ import org .scalajs .linker .interface .unstable ._
6
10
import org .scalajs .linker .standard ._
7
11
import org .scalajs .linker .standard .ModuleSet .ModuleID
8
12
9
13
import org .scalajs .linker .backend .webassembly ._
14
+ import org .scalajs .linker .backend .webassembly .Types ._
10
15
11
16
import org .scalajs .logging .Logger
12
17
13
18
import SpecialNames ._
19
+ import VarGen ._
14
20
15
21
final class Emitter (config : Emitter .Config ) {
16
22
import Emitter ._
@@ -49,7 +55,7 @@ final class Emitter(config: Emitter.Config) {
49
55
val classesWithStaticInit =
50
56
sortedClasses.filter(_.hasStaticInitializer).map(_.className)
51
57
52
- ctx. complete(
58
+ complete(
53
59
module.initializers.toList,
54
60
classesWithStaticInit,
55
61
module.topLevelExports
@@ -64,6 +70,198 @@ final class Emitter(config: Emitter.Config) {
64
70
new Result (wasmModule, loaderContent, jsFileContent)
65
71
}
66
72
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
+
67
265
private def buildJSFileContent (
68
266
module : ModuleSet .Module ,
69
267
wasmFileName : String ,
0 commit comments