| /* | |
| * Copyright 2026 WebAssembly Community Group participants | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // | |
| // Embed Wasm binaries into a JS file, or extract embedded Wasm binaries from a | |
| // JS file. | |
| // | |
| #include <iostream> | |
| #include <string> | |
| #include <vector> | |
| #include "parsing.h" | |
| #include "pass.h" | |
| #include "support/command-line.h" | |
| #include "support/file.h" | |
| #include "support/js-embedded-module.h" | |
| #include "support/utilities.h" | |
| #include "wasm-io.h" | |
| #include "wasm-validator.h" | |
| using namespace wasm; | |
| int main(int argc, const char* argv[]) { | |
| const std::string WasmEmbedOption = "wasm-embed options"; | |
| bool extract = false; | |
| std::string output; | |
| std::vector<std::string> positionals; | |
| Options options( | |
| "wasm-embed", | |
| "Embed Wasm binaries into a JS file, or extract embedded Wasm binaries " | |
| "from a JS file"); | |
| options | |
| .add("--extract", | |
| "-e", | |
| "Extract embedded Wasm modules from the input JS file into " | |
| "<output>.0.wasm, <output>.1.wasm, ...", | |
| WasmEmbedOption, | |
| Options::Arguments::Zero, | |
| [&](Options* o, const std::string& argument) { extract = true; }) | |
| .add("--output", | |
| "-o", | |
| "Output JS file (or output prefix when --extract is used)", | |
| WasmEmbedOption, | |
| Options::Arguments::One, | |
| [&](Options* o, const std::string& argument) { output = argument; }) | |
| .add_positional("INFILE.js [WASM_FILES...] [OUTFILE]", | |
| Options::Arguments::N, | |
| [&](Options* o, const std::string& argument) { | |
| positionals.push_back(argument); | |
| }); | |
| options.parse(argc, argv); | |
| if (extract) { | |
| std::string inJsFile; | |
| if (output.empty()) { | |
| if (positionals.size() != 2) { | |
| Fatal() << "Expected input JS file and output prefix"; | |
| } | |
| inJsFile = positionals[0]; | |
| output = positionals[1]; | |
| } else { | |
| if (positionals.size() != 1) { | |
| Fatal() << "Expected a single input JS file when --output is specified"; | |
| } | |
| inJsFile = positionals[0]; | |
| } | |
| auto js = read_file<std::string>(inJsFile, Flags::Text); | |
| auto modules = findEmbeddedModules(js); | |
| for (size_t i = 0; i < modules.size(); ++i) { | |
| write_file(output + "." + std::to_string(i) + ".wasm", modules[i].bytes); | |
| } | |
| flush_and_quick_exit(0); | |
| } | |
| std::string inJsFile; | |
| std::vector<std::string> wasmFiles; | |
| if (output.empty()) { | |
| if (positionals.size() < 2) { | |
| Fatal() << "Expected input JS file, wasm files, and output JS file"; | |
| } | |
| inJsFile = positionals.front(); | |
| output = positionals.back(); | |
| wasmFiles.assign(positionals.begin() + 1, positionals.end() - 1); | |
| } else { | |
| if (positionals.empty()) { | |
| Fatal() << "Expected input JS file"; | |
| } | |
| inJsFile = positionals.front(); | |
| wasmFiles.assign(positionals.begin() + 1, positionals.end()); | |
| } | |
| auto js = read_file<std::string>(inJsFile, Flags::Text); | |
| auto existing = findEmbeddedModules(js); | |
| if (existing.size() != wasmFiles.size()) { | |
| Fatal() << "Number of embedded wasm modules in " << inJsFile << " (" | |
| << existing.size() | |
| << ") does not match number of provided wasm files (" | |
| << wasmFiles.size() << ")"; | |
| } | |
| for (size_t i = existing.size(); i > 0; --i) { | |
| const auto& mod = existing[i - 1]; | |
| const auto& wasmFile = wasmFiles[i - 1]; | |
| auto bytes = read_file<std::vector<char>>(wasmFile, Flags::Binary); | |
| bool isBinary = bytes.size() >= 4 && bytes[0] == '\0' && bytes[1] == 'a' && | |
| bytes[2] == 's' && bytes[3] == 'm'; | |
| Module wasm; | |
| wasm.features = FeatureSet::All; | |
| try { | |
| ModuleReader().readData(bytes, wasm); | |
| } catch (ParseException& p) { | |
| p.dump(std::cerr); | |
| std::cerr << '\n'; | |
| Fatal() << "error parsing wasm (" << wasmFile << ")"; | |
| } | |
| if (!WasmValidator().validate(wasm)) { | |
| Fatal() << "error validating module (" << wasmFile << ")"; | |
| } | |
| if (!isBinary) { | |
| PassOptions passOptions; | |
| ModuleWriter writer(passOptions); | |
| writer.setBinary(true); | |
| writer.write(wasm, bytes); | |
| } | |
| js.replace(mod.start, mod.end - mod.start, formatByteArray(bytes)); | |
| } | |
| write_file(output, js); | |
| flush_and_quick_exit(0); | |
| } |