| /* |
| * 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); |
| } |