blob: 26b2bbad06f13a70cdedd585fa383c3695e52a48 [file] [log] [blame] [edit]
/*
* Copyright 2023 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 "wat-parser.h"
#include "contexts.h"
#include "ir/names.h"
#include "lexer.h"
#include "pass.h"
#include "wasm-type.h"
#include "wasm.h"
#include "wat-parser-internal.h"
// The WebAssembly text format is recursive in the sense that elements may be
// referred to before they are declared. Furthermore, elements may be referred
// to by index or by name. As a result, we need to parse text modules in
// multiple phases.
//
// In the first phase, we find all of the module element declarations and
// record, but do not interpret, the input spans of their corresponding
// definitions. This phase establishes the indices and names of each module
// element so that subsequent phases can look them up.
//
// The second phase parses type definitions to construct the types used in the
// module. This has to be its own phase because we have no way to refer to a
// type before it has been built along with all the other types, unlike for
// other module elements that can be referred to by name before their
// definitions have been parsed.
//
// The third phase further parses and constructs types implicitly defined by
// type uses in functions, blocks, and call_indirect instructions. These
// implicitly defined types may be referred to by index elsewhere.
//
// The fourth phase parses and sets the types of globals, functions, and other
// top-level module elements. These types need to be set before we parse
// instructions because they determine the types of instructions such as
// global.get and ref.func.
//
// The fifth and final phase parses the remaining contents of all module
// elements, including instructions.
//
// Each phase of parsing gets its own context type that is passed to the
// individual parsing functions. There is a parsing function for each element of
// the grammar given in the spec. Parsing functions are templatized so that they
// may be passed the appropriate context type and return the correct result type
// for each phase.
namespace wasm::WATParser {
namespace {
Result<IndexMap> createIndexMap(Lexer& in, const std::vector<DefPos>& defs) {
IndexMap indices;
for (auto& def : defs) {
if (def.name.is()) {
if (!indices.insert({def.name, def.index}).second) {
return in.err(def.pos, "duplicate element name");
}
}
}
return indices;
}
void propagateDebugLocations(Module& wasm) {
// Copy debug locations from parents or previous siblings to expressions that
// do not already have their own debug locations.
PassRunner runner(&wasm);
runner.add("propagate-debug-locs");
// The parser should not be responsible for validation.
runner.setIsNested(true);
runner.run();
}
// Parse module-level declarations.
// Parse type definitions.
// Parse implicit type definitions and map typeuses without explicit types to
// the correct types.
Result<> doParseModule(Module& wasm, Lexer& input, bool allowExtra) {
ParseDeclsCtx decls(input, wasm);
CHECK_ERR(parseDecls(decls));
if (!allowExtra && !decls.in.empty()) {
return decls.in.err("Unexpected tokens after module");
}
auto typeIndices = createIndexMap(decls.in, decls.typeDefs);
CHECK_ERR(typeIndices);
std::vector<HeapType> types;
std::unordered_map<HeapType, std::unordered_map<Name, Index>> typeNames;
CHECK_ERR(parseTypeDefs(decls, input, *typeIndices, types, typeNames));
std::unordered_map<Index, HeapType> implicitTypes;
CHECK_ERR(
parseImplicitTypeDefs(decls, input, *typeIndices, types, implicitTypes));
CHECK_ERR(parseModuleTypes(decls, input, *typeIndices, types, implicitTypes));
CHECK_ERR(parseDefinitions(
decls, input, *typeIndices, types, implicitTypes, typeNames));
propagateDebugLocations(wasm);
input = decls.in;
return Ok{};
}
} // anonymous namespace
Result<> parseModule(Module& wasm,
std::string_view in,
std::optional<std::string> filename) {
Lexer lexer(in, filename);
return doParseModule(wasm, lexer, false);
}
Result<> parseModule(Module& wasm, std::string_view in) {
Lexer lexer(in);
return doParseModule(wasm, lexer, false);
}
Result<> parseModule(Module& wasm, Lexer& lexer) {
return doParseModule(wasm, lexer, true);
}
} // namespace wasm::WATParser