| /* | |
| * 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. | |
| */ | |
| #include <iostream> | |
| #include <string> | |
| #include "parser/wat-parser.h" | |
| #include "support/file.h" | |
| #include "tools/wasm2c/assertion-emitter.h" | |
| #include "tools/wasm2c/c-printer.h" | |
| #include "wasm.h" | |
| // code to be inserted into the generated output | |
| extern const char* SpecTop; | |
| namespace wasm { | |
| namespace { | |
| inline std::string stripExtension(const std::string& path) { | |
| size_t lastDot = path.find_last_of('.'); | |
| if (lastDot == std::string::npos) { | |
| return path; | |
| } | |
| size_t lastSlash = path.find_last_of("/\\"); | |
| if (lastSlash != std::string::npos && lastDot < lastSlash) { | |
| return path; | |
| } | |
| return path.substr(0, lastDot); | |
| } | |
| inline std::string getBasename(const std::string& path) { | |
| size_t lastSlash = path.find_last_of("/\\"); | |
| if (lastSlash == std::string::npos) { | |
| return path; | |
| } | |
| return path.substr(lastSlash + 1); | |
| } | |
| } // anonymous namespace | |
| AssertionEmitter::AssertionEmitter(WATParser::WASTScript& script, | |
| Wasm2CBuilder::Flags flags) | |
| : script(script), flags(flags) {} | |
| void AssertionEmitter::emit(std::ostream& cOut, | |
| const std::string& outputCPath) { | |
| CPrinter c(cOut); | |
| std::string basePath = | |
| outputCPath.empty() ? "spec" : stripExtension(outputCPath); | |
| std::string baseBasename = getBasename(basePath); | |
| // Loop sequentially through WASTScript AST commands | |
| for (size_t i = 0; i < script.size(); i++) { | |
| auto& entry = script[i]; | |
| auto& cmd = entry.cmd; | |
| if (auto* mod = std::get_if<WATParser::WASTModule>(&cmd)) { | |
| if (mod->isDefinition) { | |
| Fatal() << "Module definition is not supported"; | |
| } | |
| auto* w = std::get_if<std::shared_ptr<Module>>(&mod->module); | |
| assert(w && "expected parsed Module pointer inside WASTModule"); | |
| auto wasm = *w; | |
| size_t currentIdx = moduleCounter++; | |
| std::string prefix = "spec_" + std::to_string(currentIdx); | |
| // Generate separate files for this module | |
| std::string modHFilename = | |
| baseBasename + "." + std::to_string(currentIdx) + ".h"; | |
| std::string modCPath = basePath + "." + std::to_string(currentIdx) + ".c"; | |
| std::string modHPath = basePath + "." + std::to_string(currentIdx) + ".h"; | |
| Output modCOut(modCPath, Flags::Text); | |
| Output modHOut(modHPath, Flags::Text); | |
| Wasm2CBuilder::Flags modFlags = flags; | |
| modFlags.moduleName = prefix; | |
| modFlags.headerName = modHFilename; | |
| Wasm2CBuilder builder(modFlags); | |
| builder.processWasm(wasm.get(), modCOut.getStream(), modHOut.getStream()); | |
| c << "#include \"" << modHFilename << "\"" << endl; | |
| c << SpecTop << endl << endl; | |
| } else if (std::get_if<WATParser::Register>(&cmd)) { | |
| Fatal() << "register is not yet supported"; | |
| } else if (std::get_if<WATParser::Assertion>(&cmd)) { | |
| Fatal() << "assertions are not yet supported"; | |
| } else { | |
| Fatal() << "unsupported command"; | |
| } | |
| } | |
| // Write main execution entry point | |
| c << "void run_spec_tests() {" << endl; | |
| c << "}" << endl; | |
| } | |
| } // namespace wasm |