WebAssemblyJavascriptGenerator.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { RawSource } = require("webpack-sources");
  7. const { UsageState } = require("../ExportsInfo");
  8. const Generator = require("../Generator");
  9. const InitFragment = require("../InitFragment");
  10. const { WEBASSEMBLY_TYPES } = require("../ModuleSourceTypeConstants");
  11. const RuntimeGlobals = require("../RuntimeGlobals");
  12. const Template = require("../Template");
  13. const ModuleDependency = require("../dependencies/ModuleDependency");
  14. const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
  15. const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
  16. /** @typedef {import("webpack-sources").Source} Source */
  17. /** @typedef {import("../Generator").GenerateContext} GenerateContext */
  18. /** @typedef {import("../Module")} Module */
  19. /** @typedef {import("../Module").SourceType} SourceType */
  20. /** @typedef {import("../Module").SourceTypes} SourceTypes */
  21. /** @typedef {import("../NormalModule")} NormalModule */
  22. class WebAssemblyJavascriptGenerator extends Generator {
  23. /**
  24. * Returns the source types available for this module.
  25. * @param {NormalModule} module fresh module
  26. * @returns {SourceTypes} available types (do not mutate)
  27. */
  28. getTypes(module) {
  29. return WEBASSEMBLY_TYPES;
  30. }
  31. /**
  32. * Returns the estimated size for the requested source type.
  33. * @param {NormalModule} module the module
  34. * @param {SourceType=} type source type
  35. * @returns {number} estimate size of the module
  36. */
  37. getSize(module, type) {
  38. return 95 + module.dependencies.length * 5;
  39. }
  40. /**
  41. * Generates generated code for this runtime module.
  42. * @param {NormalModule} module module for which the code should be generated
  43. * @param {GenerateContext} generateContext context for generate
  44. * @returns {Source | null} generated code
  45. */
  46. generate(module, generateContext) {
  47. const {
  48. runtimeTemplate,
  49. moduleGraph,
  50. chunkGraph,
  51. runtimeRequirements,
  52. runtime
  53. } = generateContext;
  54. /** @type {InitFragment<GenerateContext>[]} */
  55. const initFragments = [];
  56. const exportsInfo = moduleGraph.getExportsInfo(module);
  57. let needExportsCopy = false;
  58. /** @typedef {{ dependency: ModuleDependency | undefined, importVar: string, index: number, request: string | undefined, names: Set<string>, reexports: string[] }} ImportData */
  59. /** @type {Map<Module, ImportData>} */
  60. const importedModules = new Map();
  61. /** @type {string[]} */
  62. const initParams = [];
  63. let index = 0;
  64. for (const dep of module.dependencies) {
  65. const moduleDep =
  66. dep && dep instanceof ModuleDependency ? dep : undefined;
  67. const mod = moduleGraph.getModule(dep);
  68. if (mod) {
  69. let importData = importedModules.get(mod);
  70. if (importData === undefined) {
  71. importedModules.set(
  72. mod,
  73. (importData = {
  74. dependency: moduleDep,
  75. importVar: `m${index}`,
  76. index,
  77. request: (moduleDep && moduleDep.userRequest) || undefined,
  78. names: new Set(),
  79. reexports: []
  80. })
  81. );
  82. index++;
  83. }
  84. if (dep instanceof WebAssemblyImportDependency) {
  85. importData.names.add(dep.name);
  86. if (dep.description.type === "GlobalType") {
  87. const exportName = dep.name;
  88. const importedModule = moduleGraph.getModule(dep);
  89. if (importedModule) {
  90. const usedName = moduleGraph
  91. .getExportsInfo(importedModule)
  92. .getUsedName(exportName, runtime);
  93. if (usedName) {
  94. initParams.push(
  95. runtimeTemplate.exportFromImport({
  96. moduleGraph,
  97. chunkGraph,
  98. module: importedModule,
  99. request: dep.request,
  100. importVar: importData.importVar,
  101. originModule: module,
  102. exportName: dep.name,
  103. asiSafe: true,
  104. isCall: false,
  105. callContext: null,
  106. defaultInterop: true,
  107. initFragments,
  108. runtime,
  109. runtimeRequirements,
  110. dependency: dep
  111. })
  112. );
  113. }
  114. }
  115. }
  116. }
  117. if (dep instanceof WebAssemblyExportImportedDependency) {
  118. importData.names.add(dep.name);
  119. const usedName = moduleGraph
  120. .getExportsInfo(module)
  121. .getUsedName(dep.exportName, runtime);
  122. if (usedName) {
  123. runtimeRequirements.add(RuntimeGlobals.exports);
  124. const exportProp = `${module.exportsArgument}[${JSON.stringify(
  125. usedName
  126. )}]`;
  127. const defineStatement = Template.asString([
  128. `${exportProp} = ${runtimeTemplate.exportFromImport({
  129. moduleGraph,
  130. module: /** @type {Module} */ (moduleGraph.getModule(dep)),
  131. chunkGraph,
  132. request: dep.request,
  133. importVar: importData.importVar,
  134. originModule: module,
  135. exportName: dep.name,
  136. asiSafe: true,
  137. isCall: false,
  138. callContext: null,
  139. defaultInterop: true,
  140. initFragments,
  141. runtime,
  142. runtimeRequirements,
  143. dependency: dep
  144. })};`,
  145. `if(WebAssembly.Global) ${exportProp} = ` +
  146. `new WebAssembly.Global({ value: ${JSON.stringify(
  147. dep.valueType
  148. )} }, ${exportProp});`
  149. ]);
  150. importData.reexports.push(defineStatement);
  151. needExportsCopy = true;
  152. }
  153. }
  154. }
  155. }
  156. const importsCode = Template.asString(
  157. Array.from(
  158. importedModules,
  159. ([module, { importVar, request, reexports, dependency }]) => {
  160. const importStatement = runtimeTemplate.importStatement({
  161. module,
  162. moduleGraph,
  163. chunkGraph,
  164. request,
  165. importVar,
  166. originModule: module,
  167. runtimeRequirements,
  168. dependency
  169. });
  170. return importStatement[0] + importStatement[1] + reexports.join("\n");
  171. }
  172. )
  173. );
  174. const copyAllExports =
  175. exportsInfo.otherExportsInfo.getUsed(runtime) === UsageState.Unused &&
  176. !needExportsCopy;
  177. // need these globals
  178. runtimeRequirements.add(RuntimeGlobals.module);
  179. runtimeRequirements.add(RuntimeGlobals.moduleId);
  180. runtimeRequirements.add(RuntimeGlobals.wasmInstances);
  181. if (exportsInfo.otherExportsInfo.getUsed(runtime) !== UsageState.Unused) {
  182. runtimeRequirements.add(RuntimeGlobals.makeNamespaceObject);
  183. runtimeRequirements.add(RuntimeGlobals.exports);
  184. }
  185. if (!copyAllExports) {
  186. runtimeRequirements.add(RuntimeGlobals.exports);
  187. }
  188. // create source
  189. const source = new RawSource(
  190. [
  191. '"use strict";',
  192. "// Instantiate WebAssembly module",
  193. `var wasmExports = ${RuntimeGlobals.wasmInstances}[${module.moduleArgument}.id];`,
  194. exportsInfo.otherExportsInfo.getUsed(runtime) !== UsageState.Unused
  195. ? `${RuntimeGlobals.makeNamespaceObject}(${module.exportsArgument});`
  196. : "",
  197. // this must be before import for circular dependencies
  198. "// export exports from WebAssembly module",
  199. copyAllExports
  200. ? `${module.moduleArgument}.exports = wasmExports;`
  201. : "for(var name in wasmExports) " +
  202. "if(name) " +
  203. `${module.exportsArgument}[name] = wasmExports[name];`,
  204. "// exec imports from WebAssembly module (for esm order)",
  205. importsCode,
  206. "",
  207. "// exec wasm module",
  208. `wasmExports[""](${initParams.join(", ")})`
  209. ].join("\n")
  210. );
  211. return InitFragment.addToSource(source, initFragments, generateContext);
  212. }
  213. /**
  214. * Generates fallback output for the provided error condition.
  215. * @param {Error} error the error
  216. * @param {NormalModule} module module for which the code should be generated
  217. * @param {GenerateContext} generateContext context for generate
  218. * @returns {Source | null} generated code
  219. */
  220. generateError(error, module, generateContext) {
  221. return new RawSource(`throw new Error(${JSON.stringify(error.message)});`);
  222. }
  223. }
  224. module.exports = WebAssemblyJavascriptGenerator;