/src/WasmEdge/lib/validator/component_validator.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
3 | | |
4 | | //===-- component_validator.cpp - Component definition validation ---------===// |
5 | | // |
6 | | // The component-model validation entry point and the per-section rules. |
7 | | // |
8 | | //===----------------------------------------------------------------------===// |
9 | | |
10 | | #include "common/errinfo.h" |
11 | | #include "common/spdlog.h" |
12 | | #include "validator/component_value_decode.h" |
13 | | #include "validator/validator.h" |
14 | | |
15 | | #include <optional> |
16 | | #include <string> |
17 | | #include <unordered_set> |
18 | | #include <variant> |
19 | | |
20 | | namespace WasmEdge { |
21 | | namespace Validator { |
22 | | |
23 | | using namespace std::literals; |
24 | | |
25 | | Expect<void> |
26 | 5.98k | Validator::validate(const AST::Component::Component &Comp) noexcept { |
27 | 5.98k | CompCtx.reset(); |
28 | 5.98k | EXPECTED_TRY(validate(Comp, *CompTypes.addShape())); |
29 | | // Deferred function-body validation on the checker state saved per module. |
30 | 2.45k | for (auto &[Mod, Saved] : CompCtx.DeferredModules) { |
31 | 2.45k | Checker = std::move(Saved); |
32 | 2.45k | EXPECTED_TRY(validate(Mod->getCodeSection()).map_error([](auto E) { |
33 | 2.45k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Code)); |
34 | 2.45k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
35 | 2.45k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_CoreMod)); |
36 | 2.45k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Component)); |
37 | 2.45k | return E; |
38 | 2.45k | })); |
39 | 2.45k | const_cast<AST::Module &>(*Mod).setIsValidated(); |
40 | 2.45k | } |
41 | 2.24k | const_cast<AST::Component::Component &>(Comp).setIsValidated(); |
42 | 2.24k | return {}; |
43 | 2.24k | } |
44 | | |
45 | | Expect<void> Validator::validate(const AST::Component::Component &Comp, |
46 | 99.4k | Component::Shape &Out) noexcept { |
47 | | // Walk the sections in binary order; a reference sees only earlier entries. |
48 | 99.4k | auto &S = CompCtx.enterScope(Component::ScopeKind::Component); |
49 | 99.4k | Out.DeclScope = &S; |
50 | | |
51 | 99.4k | auto ReportError = [](auto E) { |
52 | 3.44k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Component)); |
53 | 3.44k | return E; |
54 | 3.44k | }; |
55 | 7.00M | for (const auto &Sec : Comp.getSections()) { |
56 | 7.00M | auto Visitor = [&](auto &&Section) -> Expect<void> { |
57 | 7.00M | using T = std::decay_t<decltype(Section)>; |
58 | 7.00M | if constexpr (std::is_same_v<T, AST::CustomSection>) { |
59 | 2.55M | return {}; |
60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || |
61 | 76.4k | std::is_same_v<T, AST::Component::ExportSection>) { |
62 | | // These two accumulate into the component's own external type. |
63 | 76.4k | return validate(Section, Out).map_error(ReportError); |
64 | 4.36M | } else { |
65 | 4.36M | return validate(Section).map_error(ReportError); |
66 | 4.36M | } |
67 | 7.00M | }; component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::CustomSection const&>(WasmEdge::AST::CustomSection const&) const Line | Count | Source | 56 | 2.55M | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 2.55M | using T = std::decay_t<decltype(Section)>; | 58 | 2.55M | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | 2.55M | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | | return validate(Section, Out).map_error(ReportError); | 64 | | } else { | 65 | | return validate(Section).map_error(ReportError); | 66 | | } | 67 | 2.55M | }; |
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::Component::CoreModuleSection const&>(WasmEdge::AST::Component::CoreModuleSection const&) const Line | Count | Source | 56 | 3.86k | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 3.86k | using T = std::decay_t<decltype(Section)>; | 58 | | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | | return validate(Section, Out).map_error(ReportError); | 64 | 3.86k | } else { | 65 | 3.86k | return validate(Section).map_error(ReportError); | 66 | 3.86k | } | 67 | 3.86k | }; |
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::Component::CoreInstanceSection const&>(WasmEdge::AST::Component::CoreInstanceSection const&) const Line | Count | Source | 56 | 82.8k | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 82.8k | using T = std::decay_t<decltype(Section)>; | 58 | | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | | return validate(Section, Out).map_error(ReportError); | 64 | 82.8k | } else { | 65 | 82.8k | return validate(Section).map_error(ReportError); | 66 | 82.8k | } | 67 | 82.8k | }; |
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::Component::CoreTypeSection const&>(WasmEdge::AST::Component::CoreTypeSection const&) const Line | Count | Source | 56 | 170k | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 170k | using T = std::decay_t<decltype(Section)>; | 58 | | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | | return validate(Section, Out).map_error(ReportError); | 64 | 170k | } else { | 65 | 170k | return validate(Section).map_error(ReportError); | 66 | 170k | } | 67 | 170k | }; |
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::Component::ComponentSection const&>(WasmEdge::AST::Component::ComponentSection const&) const Line | Count | Source | 56 | 93.4k | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 93.4k | using T = std::decay_t<decltype(Section)>; | 58 | | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | | return validate(Section, Out).map_error(ReportError); | 64 | 93.4k | } else { | 65 | 93.4k | return validate(Section).map_error(ReportError); | 66 | 93.4k | } | 67 | 93.4k | }; |
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::Component::InstanceSection const&>(WasmEdge::AST::Component::InstanceSection const&) const Line | Count | Source | 56 | 1.41M | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 1.41M | using T = std::decay_t<decltype(Section)>; | 58 | | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | | return validate(Section, Out).map_error(ReportError); | 64 | 1.41M | } else { | 65 | 1.41M | return validate(Section).map_error(ReportError); | 66 | 1.41M | } | 67 | 1.41M | }; |
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::Component::AliasSection const&>(WasmEdge::AST::Component::AliasSection const&) const Line | Count | Source | 56 | 5.41k | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 5.41k | using T = std::decay_t<decltype(Section)>; | 58 | | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | | return validate(Section, Out).map_error(ReportError); | 64 | 5.41k | } else { | 65 | 5.41k | return validate(Section).map_error(ReportError); | 66 | 5.41k | } | 67 | 5.41k | }; |
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::Component::TypeSection const&>(WasmEdge::AST::Component::TypeSection const&) const Line | Count | Source | 56 | 2.53M | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 2.53M | using T = std::decay_t<decltype(Section)>; | 58 | | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | | return validate(Section, Out).map_error(ReportError); | 64 | 2.53M | } else { | 65 | 2.53M | return validate(Section).map_error(ReportError); | 66 | 2.53M | } | 67 | 2.53M | }; |
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::Component::CanonSection const&>(WasmEdge::AST::Component::CanonSection const&) const Line | Count | Source | 56 | 45.1k | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 45.1k | using T = std::decay_t<decltype(Section)>; | 58 | | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | | return validate(Section, Out).map_error(ReportError); | 64 | 45.1k | } else { | 65 | 45.1k | return validate(Section).map_error(ReportError); | 66 | 45.1k | } | 67 | 45.1k | }; |
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::Component::StartSection const&>(WasmEdge::AST::Component::StartSection const&) const Line | Count | Source | 56 | 530 | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 530 | using T = std::decay_t<decltype(Section)>; | 58 | | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | | return validate(Section, Out).map_error(ReportError); | 64 | 530 | } else { | 65 | 530 | return validate(Section).map_error(ReportError); | 66 | 530 | } | 67 | 530 | }; |
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::Component::ImportSection const&>(WasmEdge::AST::Component::ImportSection const&) const Line | Count | Source | 56 | 73.0k | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 73.0k | using T = std::decay_t<decltype(Section)>; | 58 | | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | 73.0k | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | 73.0k | return validate(Section, Out).map_error(ReportError); | 64 | | } else { | 65 | | return validate(Section).map_error(ReportError); | 66 | | } | 67 | 73.0k | }; |
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::Component::ExportSection const&>(WasmEdge::AST::Component::ExportSection const&) const Line | Count | Source | 56 | 3.47k | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 3.47k | using T = std::decay_t<decltype(Section)>; | 58 | | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | 3.47k | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | 3.47k | return validate(Section, Out).map_error(ReportError); | 64 | | } else { | 65 | | return validate(Section).map_error(ReportError); | 66 | | } | 67 | 3.47k | }; |
component_validator.cpp:cxx20::expected<void, WasmEdge::ErrCode> WasmEdge::Validator::Validator::validate(WasmEdge::AST::Component::Component const&, WasmEdge::Validator::Component::Shape&)::$_0::operator()<WasmEdge::AST::Component::ValueSection const&>(WasmEdge::AST::Component::ValueSection const&) const Line | Count | Source | 56 | 13.2k | auto Visitor = [&](auto &&Section) -> Expect<void> { | 57 | 13.2k | using T = std::decay_t<decltype(Section)>; | 58 | | if constexpr (std::is_same_v<T, AST::CustomSection>) { | 59 | | return {}; | 60 | | } else if constexpr (std::is_same_v<T, AST::Component::ImportSection> || | 61 | | std::is_same_v<T, AST::Component::ExportSection>) { | 62 | | // These two accumulate into the component's own external type. | 63 | | return validate(Section, Out).map_error(ReportError); | 64 | 13.2k | } else { | 65 | 13.2k | return validate(Section).map_error(ReportError); | 66 | 13.2k | } | 67 | 13.2k | }; |
|
68 | 7.00M | EXPECTED_TRY(std::visit(Visitor, Sec)); |
69 | 7.00M | } |
70 | | // Value linearity. Every value must have exactly one consumer. |
71 | 95.9k | const auto &Values = S.Values; |
72 | 96.0k | for (uint32_t I = 0; I < Values.size(); ++I) { |
73 | 347 | if (!Values[I].Consumed) { |
74 | 304 | spdlog::error(ErrCode::Value::ComponentValueNotConsumed); |
75 | 304 | spdlog::error(" Value index {} was not consumed before the end of the " |
76 | 304 | "component."sv, |
77 | 304 | I); |
78 | 304 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Component)); |
79 | 304 | return Unexpect(ErrCode::Value::ComponentValueNotConsumed); |
80 | 304 | } |
81 | 347 | } |
82 | 95.6k | CompCtx.exitScope(); |
83 | 95.6k | return {}; |
84 | 95.9k | } |
85 | | |
86 | | Expect<void> |
87 | 3.86k | Validator::validate(const AST::Component::CoreModuleSection &ModSec) noexcept { |
88 | | // Validate every section but the function bodies, which defer to the end. |
89 | 3.86k | const auto &Mod = ModSec.getContent(); |
90 | 3.86k | Checker.reset(true); |
91 | 42.1k | auto ReportError = [](ASTNodeAttr Attr) { |
92 | 42.1k | return [Attr](auto E) { |
93 | 57 | spdlog::error(ErrInfo::InfoAST(Attr)); |
94 | 57 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
95 | 57 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_CoreMod)); |
96 | 57 | return E; |
97 | 57 | }; |
98 | 42.1k | }; |
99 | 3.86k | EXPECTED_TRY(validate(Mod.getTypeSection()) |
100 | 3.85k | .map_error(ReportError(ASTNodeAttr::Sec_Type))); |
101 | 3.85k | EXPECTED_TRY(validate(Mod.getImportSection()) |
102 | 3.84k | .map_error(ReportError(ASTNodeAttr::Sec_Import))); |
103 | 3.84k | EXPECTED_TRY(validate(Mod.getFunctionSection()) |
104 | 3.84k | .map_error(ReportError(ASTNodeAttr::Sec_Function))); |
105 | 3.84k | EXPECTED_TRY(validate(Mod.getTableSection()) |
106 | 3.83k | .map_error(ReportError(ASTNodeAttr::Sec_Table))); |
107 | 3.83k | EXPECTED_TRY(validate(Mod.getMemorySection()) |
108 | 3.82k | .map_error(ReportError(ASTNodeAttr::Sec_Memory))); |
109 | 3.82k | EXPECTED_TRY(validate(Mod.getGlobalSection()) |
110 | 3.82k | .map_error(ReportError(ASTNodeAttr::Sec_Global))); |
111 | 3.82k | EXPECTED_TRY(validate(Mod.getTagSection()) |
112 | 3.81k | .map_error(ReportError(ASTNodeAttr::Sec_Tag))); |
113 | 3.81k | EXPECTED_TRY(validate(Mod.getExportSection()) |
114 | 3.81k | .map_error(ReportError(ASTNodeAttr::Sec_Export))); |
115 | 3.81k | EXPECTED_TRY(validate(Mod.getStartSection()) |
116 | 3.81k | .map_error(ReportError(ASTNodeAttr::Sec_Start))); |
117 | 3.81k | EXPECTED_TRY(validate(Mod.getElementSection()) |
118 | 3.81k | .map_error(ReportError(ASTNodeAttr::Sec_Element))); |
119 | 3.81k | EXPECTED_TRY(validate(Mod.getDataSection()) |
120 | 3.81k | .map_error(ReportError(ASTNodeAttr::Sec_Data))); |
121 | 3.81k | if (Checker.getTables().size() > 1 && |
122 | 1.05k | !Conf.hasProposal(Proposal::ReferenceTypes)) { |
123 | 0 | spdlog::error(ErrCode::Value::MultiTables); |
124 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::ReferenceTypes)); |
125 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
126 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_CoreMod)); |
127 | 0 | return Unexpect(ErrCode::Value::MultiTables); |
128 | 0 | } |
129 | 3.81k | if (Checker.getMemories().size() > 1 && |
130 | 114 | !Conf.hasProposal(Proposal::MultiMemories)) { |
131 | 0 | spdlog::error(ErrCode::Value::MultiMemories); |
132 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::MultiMemories)); |
133 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
134 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_CoreMod)); |
135 | 0 | return Unexpect(ErrCode::Value::MultiMemories); |
136 | 0 | } |
137 | 3.81k | CompCtx.DeferredModules.emplace_back(&Mod, Checker); |
138 | 3.81k | EXPECTED_TRY(const auto *Shape, CompCtx.buildCoreShape(Mod)); |
139 | 3.80k | CompCtx.top().addCoreModule(Shape); |
140 | 3.80k | return {}; |
141 | 3.81k | } |
142 | | |
143 | | Expect<void> Validator::validate( |
144 | 82.8k | const AST::Component::CoreInstanceSection &InstSec) noexcept { |
145 | 84.3k | for (const auto &Inst : InstSec.getContent()) { |
146 | 84.3k | EXPECTED_TRY(validate(Inst).map_error([](auto E) { |
147 | 84.3k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_CoreInstance)); |
148 | 84.3k | return E; |
149 | 84.3k | })); |
150 | 84.3k | } |
151 | 82.7k | return {}; |
152 | 82.8k | } |
153 | | |
154 | | Expect<void> |
155 | 84.3k | Validator::validate(const AST::Component::CoreInstance &Inst) noexcept { |
156 | 84.3k | auto &S = CompCtx.top(); |
157 | 84.3k | if (Inst.isInstantiateModule()) { |
158 | 495 | const auto *Mod = S.getCoreModule(Inst.getModuleIndex()); |
159 | 495 | if (Mod == nullptr) { |
160 | 19 | spdlog::error(ErrCode::Value::ComponentUnknownModule); |
161 | 19 | spdlog::error(" Core module index {} out of bounds (size {})."sv, |
162 | 19 | Inst.getModuleIndex(), S.CoreModules.size()); |
163 | 19 | return Unexpect(ErrCode::Value::ComponentUnknownModule); |
164 | 19 | } |
165 | | // Collect the named argument instances. |
166 | 476 | std::unordered_map<std::string_view, const Component::CoreShape *> Args; |
167 | 476 | for (const auto &Arg : Inst.getInstantiateArgs()) { |
168 | 0 | const auto *AI = S.getCoreInstance(Arg.getIndex()); |
169 | 0 | if (AI == nullptr) { |
170 | 0 | spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds); |
171 | 0 | spdlog::error(" Core instance index {} out of bounds (size {})."sv, |
172 | 0 | Arg.getIndex(), S.CoreInstances.size()); |
173 | 0 | return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds); |
174 | 0 | } |
175 | 0 | if (!Args.emplace(Arg.getName(), AI).second) { |
176 | 0 | spdlog::error(ErrCode::Value::ComponentDuplicateModuleArg); |
177 | 0 | spdlog::error(" Duplicate instantiation argument '{}'."sv, |
178 | 0 | Arg.getName()); |
179 | 0 | return Unexpect(ErrCode::Value::ComponentDuplicateModuleArg); |
180 | 0 | } |
181 | 0 | } |
182 | | // Every module import must be satisfied by the matching argument. |
183 | 476 | for (const auto &[ModName, Name, Required] : Mod->Imports) { |
184 | 0 | auto It = Args.find(ModName); |
185 | 0 | if (It == Args.end()) { |
186 | 0 | spdlog::error(ErrCode::Value::ComponentMissingModuleArg); |
187 | 0 | spdlog::error(" Missing instantiation argument '{}'."sv, ModName); |
188 | 0 | return Unexpect(ErrCode::Value::ComponentMissingModuleArg); |
189 | 0 | } |
190 | 0 | auto ExpIt = It->second->Exports.find(Name); |
191 | 0 | if (ExpIt == It->second->Exports.end()) { |
192 | 0 | spdlog::error(ErrCode::Value::ComponentUnknownExport); |
193 | 0 | spdlog::error( |
194 | 0 | " Instance argument '{}' does not export '{}' required by " |
195 | 0 | "the module."sv, |
196 | 0 | ModName, Name); |
197 | 0 | return Unexpect(ErrCode::Value::ComponentUnknownExport); |
198 | 0 | } |
199 | 0 | const auto &Provided = ExpIt->second; |
200 | 0 | if (Provided.Kind != Required.Kind) { |
201 | 0 | ErrCode::Value Code = ErrCode::Value::ArgTypeMismatch; |
202 | 0 | if (Required.Kind == ExternalType::Global) { |
203 | 0 | Code = ErrCode::Value::ComponentExpectedGlobal; |
204 | 0 | } else if (Required.Kind == ExternalType::Function) { |
205 | 0 | Code = ErrCode::Value::ComponentExpectedFunc; |
206 | 0 | } |
207 | 0 | spdlog::error(Code); |
208 | 0 | spdlog::error(" Import '{}'.'{}' kind mismatch."sv, ModName, Name); |
209 | 0 | return Unexpect(Code); |
210 | 0 | } |
211 | 0 | if (!Component::Matcher(CompTypes).matchCoreExtern(Provided, Required)) { |
212 | | // Distinguish the mismatch class for diagnostics. |
213 | 0 | ErrCode::Value Code = ErrCode::Value::ArgTypeMismatch; |
214 | 0 | switch (Required.Kind) { |
215 | 0 | case ExternalType::Function: |
216 | | // A trivial expected signature is reported as `expected: (func)`. |
217 | 0 | if (Required.Func != nullptr && |
218 | 0 | Required.Func->getCompositeType().isFunc() && |
219 | 0 | Required.Func->getCompositeType() |
220 | 0 | .getFuncType() |
221 | 0 | .getParamTypes() |
222 | 0 | .empty() && |
223 | 0 | Required.Func->getCompositeType() |
224 | 0 | .getFuncType() |
225 | 0 | .getReturnTypes() |
226 | 0 | .empty()) { |
227 | 0 | Code = ErrCode::Value::ComponentExpectedFuncParen; |
228 | 0 | } |
229 | 0 | break; |
230 | 0 | case ExternalType::Memory: |
231 | 0 | if (Provided.Memory != nullptr && Required.Memory != nullptr) { |
232 | 0 | const auto &LP = Provided.Memory->getLimit(); |
233 | 0 | const auto &LR = Required.Memory->getLimit(); |
234 | 0 | if (LP.isShared() != LR.isShared()) { |
235 | 0 | Code = ErrCode::Value::ComponentMemorySharedMismatch; |
236 | 0 | } else if (LP.is64() != LR.is64()) { |
237 | 0 | Code = ErrCode::Value::ComponentMemoryIndexTypeMismatch; |
238 | 0 | } else { |
239 | 0 | Code = ErrCode::Value::ComponentMemoryLimitsMismatch; |
240 | 0 | } |
241 | 0 | } |
242 | 0 | break; |
243 | 0 | case ExternalType::Global: |
244 | 0 | Code = ErrCode::Value::ArgTypeMismatch; |
245 | 0 | break; |
246 | 0 | case ExternalType::Table: |
247 | 0 | if (Provided.Table != nullptr && Required.Table != nullptr && |
248 | 0 | !(Provided.Table->getRefType() == Required.Table->getRefType())) { |
249 | 0 | Code = ErrCode::Value::ArgTypeMismatch; |
250 | 0 | } else { |
251 | 0 | Code = ErrCode::Value::ComponentTableLimitsMismatch; |
252 | 0 | } |
253 | 0 | break; |
254 | 0 | default: |
255 | 0 | break; |
256 | 0 | } |
257 | 0 | spdlog::error(Code); |
258 | 0 | spdlog::error(" Import '{}'.'{}' type mismatch."sv, ModName, Name); |
259 | 0 | return Unexpect(Code); |
260 | 0 | } |
261 | 0 | } |
262 | | // The new core instance exposes the module's exports. |
263 | 476 | auto *Result = CompTypes.addCoreShape(); |
264 | 476 | Result->Exports = Mod->Exports; |
265 | 476 | S.addCoreInstance(Result); |
266 | 476 | return {}; |
267 | 476 | } |
268 | | |
269 | | // Inline exports project index-space entries into a fresh core instance. |
270 | 83.8k | auto *Result = CompTypes.addCoreShape(); |
271 | 83.8k | for (const auto &Exp : Inst.getInlineExports()) { |
272 | 4.13k | const auto &SI = Exp.getSortIdx(); |
273 | 4.13k | const uint32_t Idx = SI.getIdx(); |
274 | 4.13k | if (!SI.getSort().isCore()) { |
275 | 0 | spdlog::error(ErrCode::Value::InvalidTypeReference); |
276 | 0 | return Unexpect(ErrCode::Value::InvalidTypeReference); |
277 | 0 | } |
278 | 4.13k | Component::CoreExternInfo Ext; |
279 | 4.13k | switch (SI.getSort().getCoreSortType()) { |
280 | 4.08k | case AST::Component::Sort::CoreSortType::Func: { |
281 | 4.08k | const auto *F = S.getCoreFunc(Idx); |
282 | 4.08k | if (F == nullptr) { |
283 | 20 | spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds); |
284 | 20 | spdlog::error(" Core function index {} out of bounds."sv, Idx); |
285 | 20 | return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds); |
286 | 20 | } |
287 | 4.06k | Ext = Component::CoreExternInfo(ExternalType::Function, F); |
288 | 4.06k | break; |
289 | 4.08k | } |
290 | 7 | case AST::Component::Sort::CoreSortType::Table: { |
291 | 7 | if (Idx >= S.CoreTables.size()) { |
292 | 7 | spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds); |
293 | 7 | spdlog::error(" Core table index {} out of bounds."sv, Idx); |
294 | 7 | return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds); |
295 | 7 | } |
296 | 0 | Ext = Component::CoreExternInfo(S.CoreTables[Idx]); |
297 | 0 | break; |
298 | 7 | } |
299 | 15 | case AST::Component::Sort::CoreSortType::Memory: { |
300 | 15 | if (Idx >= S.CoreMemories.size()) { |
301 | 15 | spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds); |
302 | 15 | spdlog::error(" Core memory index {} out of bounds."sv, Idx); |
303 | 15 | return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds); |
304 | 15 | } |
305 | 0 | Ext = Component::CoreExternInfo(S.CoreMemories[Idx]); |
306 | 0 | break; |
307 | 15 | } |
308 | 14 | case AST::Component::Sort::CoreSortType::Global: { |
309 | 14 | if (Idx >= S.CoreGlobals.size()) { |
310 | 14 | spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds); |
311 | 14 | spdlog::error(" Core global index {} out of bounds."sv, Idx); |
312 | 14 | return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds); |
313 | 14 | } |
314 | 0 | Ext = Component::CoreExternInfo(S.CoreGlobals[Idx]); |
315 | 0 | break; |
316 | 14 | } |
317 | 20 | case AST::Component::Sort::CoreSortType::Tag: { |
318 | 20 | if (Idx >= S.CoreTags.size()) { |
319 | 20 | spdlog::error(ErrCode::Value::UnknownCoreTag); |
320 | 20 | spdlog::error(" Core tag index {} out of bounds."sv, Idx); |
321 | 20 | return Unexpect(ErrCode::Value::UnknownCoreTag); |
322 | 20 | } |
323 | 0 | Ext = Component::CoreExternInfo(ExternalType::Tag, S.CoreTags[Idx]); |
324 | 0 | break; |
325 | 20 | } |
326 | 1 | default: |
327 | 1 | spdlog::error(ErrCode::Value::InvalidTypeReference); |
328 | 1 | spdlog::error( |
329 | 1 | " Core instances can only export functions, tables, memories, " |
330 | 1 | "globals, and tags."sv); |
331 | 1 | return Unexpect(ErrCode::Value::InvalidTypeReference); |
332 | 4.13k | } |
333 | 4.06k | if (!Result->Exports.emplace(std::string(Exp.getName()), Ext).second) { |
334 | 1 | spdlog::error(ErrCode::Value::DupExportName); |
335 | 1 | spdlog::error(" Duplicate inline export name '{}'."sv, Exp.getName()); |
336 | 1 | return Unexpect(ErrCode::Value::DupExportName); |
337 | 1 | } |
338 | 4.06k | } |
339 | 83.7k | S.addCoreInstance(Result); |
340 | 83.7k | return {}; |
341 | 83.8k | } |
342 | | |
343 | | Expect<void> |
344 | 170k | Validator::validate(const AST::Component::CoreTypeSection &TypeSec) noexcept { |
345 | 170k | for (const auto &Type : TypeSec.getContent()) { |
346 | 125k | EXPECTED_TRY(validate(Type).map_error([](auto E) { |
347 | 125k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_CoreType)); |
348 | 125k | return E; |
349 | 125k | })); |
350 | 125k | } |
351 | 170k | return {}; |
352 | 170k | } |
353 | | |
354 | | Expect<void> |
355 | 93.4k | Validator::validate(const AST::Component::ComponentSection &CompSec) noexcept { |
356 | 93.4k | auto *Shape = CompTypes.addShape(); |
357 | 93.4k | EXPECTED_TRY(validate(CompSec.getContent(), *Shape).map_error([](auto E) { |
358 | 93.4k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Component)); |
359 | 93.4k | return E; |
360 | 93.4k | })); |
361 | 93.4k | CompCtx.top().addComponent(Shape); |
362 | 93.4k | return {}; |
363 | 93.4k | } |
364 | | |
365 | | Expect<void> |
366 | 1.41M | Validator::validate(const AST::Component::InstanceSection &InstSec) noexcept { |
367 | 1.41M | for (const auto &Inst : InstSec.getContent()) { |
368 | 1.36M | EXPECTED_TRY(validate(Inst).map_error([](auto E) { |
369 | 1.36M | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Instance)); |
370 | 1.36M | return E; |
371 | 1.36M | })); |
372 | 1.36M | } |
373 | 1.41M | return {}; |
374 | 1.41M | } |
375 | | |
376 | | Expect<void> |
377 | 1.36M | Validator::validate(const AST::Component::Instance &Inst) noexcept { |
378 | 1.36M | auto &S = CompCtx.top(); |
379 | 1.36M | if (Inst.isInstantiateModule()) { |
380 | 283k | const auto *CI = S.getComponent(Inst.getComponentIndex()); |
381 | 283k | if (CI == nullptr) { |
382 | 43 | spdlog::error(ErrCode::Value::ComponentUnknownComponent); |
383 | 43 | spdlog::error(" Component index {} out of bounds (size {})."sv, |
384 | 43 | Inst.getComponentIndex(), S.Components.size()); |
385 | 43 | return Unexpect(ErrCode::Value::ComponentUnknownComponent); |
386 | 43 | } |
387 | 283k | EXPECTED_TRY(const auto *Result, CompCtx.instantiateComponentShape( |
388 | 283k | *CI, Inst.getInstantiateArgs())); |
389 | 283k | CompCtx.top().addInstance(Result); |
390 | 283k | return {}; |
391 | 283k | } |
392 | | |
393 | | // Inline exports. Index validity is checked before the export name. |
394 | 1.07M | auto *Result = CompTypes.addShape(); |
395 | 1.07M | Result->DeclScope = &S; |
396 | 1.07M | std::vector<Component::NameRecord> Names; |
397 | 1.07M | for (const auto &Exp : Inst.getInlineExports()) { |
398 | | // Function indices get their own diagnostic here. |
399 | 281 | if (!Exp.getSortIdx().getSort().isCore() && |
400 | 274 | Exp.getSortIdx().getSort().getSortType() == |
401 | 274 | AST::Component::Sort::SortType::Func && |
402 | 83 | S.getFunc(Exp.getSortIdx().getIdx()) == nullptr) { |
403 | 8 | spdlog::error(ErrCode::Value::ComponentFunctionIndexOutOfBounds); |
404 | 8 | spdlog::error(" Function index {} out of bounds (size {})."sv, |
405 | 8 | Exp.getSortIdx().getIdx(), S.Funcs.size()); |
406 | 8 | return Unexpect(ErrCode::Value::ComponentFunctionIndexOutOfBounds); |
407 | 8 | } |
408 | 533 | EXPECTED_TRY(auto Info, CompCtx.resolveSortIndex(Exp.getSortIdx())); |
409 | 533 | EXPECTED_TRY(Component::ExternName CN, |
410 | 256 | CompCtx.parseExternName(Exp.getName(), false)); |
411 | 256 | EXPECTED_TRY( |
412 | 256 | CompCtx.addUniqueName(Names, CompCtx.makeNameRecord(CN), false)); |
413 | 256 | EXPECTED_TRY(CompCtx.checkAnnotatedName(CN, Info, false)); |
414 | 256 | EXPECTED_TRY(CompCtx.checkNameAttributes( |
415 | 256 | CN, Exp.getImplements(), Exp.getExternalIds(), Exp.getVersionSuffixes(), |
416 | 256 | Info.Kind == Component::ExternKind::InstanceType)); |
417 | 256 | if (!Exp.getSortIdx().getSort().isCore() && |
418 | 256 | Exp.getSortIdx().getSort().getSortType() == |
419 | 256 | AST::Component::Sort::SortType::Value) { |
420 | 26 | EXPECTED_TRY(CompCtx.top().consumeValue(Exp.getSortIdx().getIdx())); |
421 | 26 | } |
422 | 253 | Result->Exports.emplace(std::string(Exp.getName()), Info); |
423 | 253 | Result->ExportOrder.emplace_back(Exp.getName()); |
424 | 253 | } |
425 | | // An inline instance nests re-exported shapes, so it is bounded like a type. |
426 | 1.07M | Component::ExternInfo Probe; |
427 | 1.07M | Probe.Kind = Component::ExternKind::InstanceType; |
428 | 1.07M | Probe.Shape = Result; |
429 | 1.07M | EXPECTED_TRY(CompTypes.checkTypeLimits(Probe)); |
430 | 1.07M | CompCtx.top().addInstance(Result); |
431 | 1.07M | return {}; |
432 | 1.07M | } |
433 | | |
434 | | Expect<void> |
435 | 5.41k | Validator::validate(const AST::Component::AliasSection &AliasSec) noexcept { |
436 | 5.41k | for (const auto &Alias : AliasSec.getContent()) { |
437 | 4.76k | EXPECTED_TRY(validate(Alias).map_error([](auto E) { |
438 | 4.76k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Alias)); |
439 | 4.76k | return E; |
440 | 4.76k | })); |
441 | 4.76k | } |
442 | 5.26k | return {}; |
443 | 5.41k | } |
444 | | |
445 | 5.40k | Expect<void> Validator::validate(const AST::Component::Alias &Alias) noexcept { |
446 | 5.40k | auto &S = CompCtx.top(); |
447 | 5.40k | const auto &Sort = Alias.getSort(); |
448 | 5.40k | const bool InTypeDecl = S.Kind == Component::ScopeKind::ComponentType || |
449 | 5.05k | S.Kind == Component::ScopeKind::InstanceType; |
450 | | |
451 | 5.40k | switch (Alias.getTargetType()) { |
452 | 33 | case AST::Component::Alias::TargetType::Export: { |
453 | 33 | if (Sort.isCore() && |
454 | 8 | Sort.getCoreSortType() != AST::Component::Sort::CoreSortType::Module) { |
455 | 4 | spdlog::error(ErrCode::Value::MalformedAliasTarget); |
456 | 4 | return Unexpect(ErrCode::Value::MalformedAliasTarget); |
457 | 4 | } |
458 | 29 | const bool WantCoreModule = Sort.isCore(); |
459 | 29 | if (InTypeDecl && |
460 | 10 | (WantCoreModule || |
461 | 9 | (Sort.getSortType() != AST::Component::Sort::SortType::Type && |
462 | 2 | Sort.getSortType() != AST::Component::Sort::SortType::Instance))) { |
463 | 2 | spdlog::error(ErrCode::Value::InvalidTypeReference); |
464 | 2 | spdlog::error(" Aliases in a component or instance type may only " |
465 | 2 | "refer to types or instances."sv); |
466 | 2 | return Unexpect(ErrCode::Value::InvalidTypeReference); |
467 | 2 | } |
468 | 27 | const auto &[InstIdx, Name] = Alias.getExport(); |
469 | 27 | const auto *Inst = S.getInstance(InstIdx); |
470 | 27 | if (Inst == nullptr) { |
471 | 25 | spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds); |
472 | 25 | spdlog::error(" Instance index {} out of bounds (size {})."sv, InstIdx, |
473 | 25 | S.Instances.size()); |
474 | 25 | return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds); |
475 | 25 | } |
476 | 2 | auto It = Inst->Exports.find(Name); |
477 | 2 | if (WantCoreModule) { |
478 | | // Core-module exports of component instances. |
479 | 0 | if (It == Inst->Exports.end() || |
480 | 0 | It->second.Kind != Component::ExternKind::CoreType) { |
481 | 0 | spdlog::error(ErrCode::Value::ComponentExportNotAModule); |
482 | 0 | spdlog::error(" Export '{}' of instance {} is not a core module."sv, |
483 | 0 | Name, InstIdx); |
484 | 0 | return Unexpect(ErrCode::Value::ComponentExportNotAModule); |
485 | 0 | } |
486 | 0 | CompCtx.defineExtern(It->second); |
487 | 0 | return {}; |
488 | 0 | } |
489 | 2 | if (It == Inst->Exports.end()) { |
490 | 2 | spdlog::error(ErrCode::Value::ComponentUnknownExport); |
491 | 2 | spdlog::error(" Instance {} has no export named '{}'."sv, InstIdx, |
492 | 2 | Name); |
493 | 2 | return Unexpect(ErrCode::Value::ComponentUnknownExport); |
494 | 2 | } |
495 | 0 | const auto ST = Sort.getSortType(); |
496 | 0 | const auto &Info = It->second; |
497 | 0 | const bool KindOk = (ST == AST::Component::Sort::SortType::Func && |
498 | 0 | Info.Kind == Component::ExternKind::FuncType) || |
499 | 0 | (ST == AST::Component::Sort::SortType::Value && |
500 | 0 | Info.Kind == Component::ExternKind::ValueBound) || |
501 | 0 | (ST == AST::Component::Sort::SortType::Type && |
502 | 0 | Info.Kind == Component::ExternKind::TypeBound) || |
503 | 0 | (ST == AST::Component::Sort::SortType::Component && |
504 | 0 | Info.Kind == Component::ExternKind::ComponentType) || |
505 | 0 | (ST == AST::Component::Sort::SortType::Instance && |
506 | 0 | Info.Kind == Component::ExternKind::InstanceType); |
507 | 0 | if (!KindOk) { |
508 | 0 | spdlog::error(ErrCode::Value::ComponentUnknownExport); |
509 | 0 | spdlog::error(" Export '{}' of instance {} does not match the " |
510 | 0 | "alias sort."sv, |
511 | 0 | Name, InstIdx); |
512 | 0 | return Unexpect(ErrCode::Value::ComponentUnknownExport); |
513 | 0 | } |
514 | 0 | CompCtx.defineExtern(Info); |
515 | 0 | return {}; |
516 | 0 | } |
517 | 599 | case AST::Component::Alias::TargetType::CoreExport: { |
518 | 599 | if (!Sort.isCore()) { |
519 | 10 | spdlog::error(ErrCode::Value::MalformedAliasTarget); |
520 | 10 | return Unexpect(ErrCode::Value::MalformedAliasTarget); |
521 | 10 | } |
522 | 589 | if (InTypeDecl) { |
523 | 1 | spdlog::error(ErrCode::Value::InvalidTypeReference); |
524 | 1 | spdlog::error(" Aliases in a component or instance type may only " |
525 | 1 | "refer to types or instances."sv); |
526 | 1 | return Unexpect(ErrCode::Value::InvalidTypeReference); |
527 | 1 | } |
528 | 588 | const auto &[InstIdx, Name] = Alias.getExport(); |
529 | 588 | const auto *Inst = S.getCoreInstance(InstIdx); |
530 | 588 | if (Inst == nullptr) { |
531 | 25 | spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds); |
532 | 25 | spdlog::error(" Core instance index {} out of bounds (size {})."sv, |
533 | 25 | InstIdx, S.CoreInstances.size()); |
534 | 25 | return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds); |
535 | 25 | } |
536 | 563 | auto It = Inst->Exports.find(Name); |
537 | 563 | const auto CS = Sort.getCoreSortType(); |
538 | 563 | if (It == Inst->Exports.end()) { |
539 | 18 | const auto Code = CS == AST::Component::Sort::CoreSortType::Tag |
540 | 18 | ? ErrCode::Value::UnknownCoreTag |
541 | 18 | : ErrCode::Value::ComponentUnknownExport; |
542 | 18 | spdlog::error(Code); |
543 | 18 | spdlog::error(" Core instance {} has no export named '{}'."sv, InstIdx, |
544 | 18 | Name); |
545 | 18 | return Unexpect(Code); |
546 | 18 | } |
547 | 545 | const auto &Ext = It->second; |
548 | 545 | const bool KindOk = (CS == AST::Component::Sort::CoreSortType::Func && |
549 | 540 | Ext.Kind == ExternalType::Function) || |
550 | 5 | (CS == AST::Component::Sort::CoreSortType::Table && |
551 | 1 | Ext.Kind == ExternalType::Table) || |
552 | 5 | (CS == AST::Component::Sort::CoreSortType::Memory && |
553 | 1 | Ext.Kind == ExternalType::Memory) || |
554 | 5 | (CS == AST::Component::Sort::CoreSortType::Global && |
555 | 1 | Ext.Kind == ExternalType::Global) || |
556 | 5 | (CS == AST::Component::Sort::CoreSortType::Tag && |
557 | 1 | Ext.Kind == ExternalType::Tag); |
558 | 545 | if (!KindOk) { |
559 | 5 | const auto Code = CS == AST::Component::Sort::CoreSortType::Tag |
560 | 5 | ? ErrCode::Value::UnknownCoreTag |
561 | 5 | : ErrCode::Value::InvalidTypeReference; |
562 | 5 | spdlog::error(Code); |
563 | 5 | spdlog::error(" Core export '{}' does not match the alias sort."sv, |
564 | 5 | Name); |
565 | 5 | return Unexpect(Code); |
566 | 5 | } |
567 | 540 | S.addCoreExtern(Ext); |
568 | 540 | return {}; |
569 | 545 | } |
570 | 4.77k | case AST::Component::Alias::TargetType::Outer: { |
571 | 4.77k | const auto &[Ct, Idx] = Alias.getOuter(); |
572 | 4.77k | auto *Target = CompCtx.scopeUp(Ct); |
573 | 4.77k | if (Target == nullptr) { |
574 | 32 | spdlog::error(ErrCode::Value::ComponentInvalidOuterAliasCount); |
575 | 32 | spdlog::error(" Outer alias count {} exceeds enclosing scopes."sv, Ct); |
576 | 32 | return Unexpect(ErrCode::Value::ComponentInvalidOuterAliasCount); |
577 | 32 | } |
578 | 4.74k | if (Sort.isCore()) { |
579 | 492 | switch (Sort.getCoreSortType()) { |
580 | 74 | case AST::Component::Sort::CoreSortType::Module: { |
581 | 74 | if (InTypeDecl) { |
582 | 2 | spdlog::error(ErrCode::Value::InvalidTypeReference); |
583 | 2 | spdlog::error(" Aliases in a component or instance type may " |
584 | 2 | "only refer to types or instances."sv); |
585 | 2 | return Unexpect(ErrCode::Value::InvalidTypeReference); |
586 | 2 | } |
587 | 72 | const auto *Mod = Target->getCoreModule(Idx); |
588 | 72 | if (Mod == nullptr) { |
589 | 5 | spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds); |
590 | 5 | spdlog::error(" Aliased core module index {} out of bounds."sv, |
591 | 5 | Idx); |
592 | 5 | return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds); |
593 | 5 | } |
594 | 67 | S.addCoreModule(Mod); |
595 | 67 | return {}; |
596 | 72 | } |
597 | 418 | case AST::Component::Sort::CoreSortType::Type: { |
598 | 418 | const auto *Entry = Target->getCoreType(Idx); |
599 | 418 | if (Entry == nullptr) { |
600 | 7 | spdlog::error(ErrCode::Value::ComponentTypeIndexOutOfBounds); |
601 | 7 | spdlog::error(" Aliased core type index {} out of bounds."sv, Idx); |
602 | 7 | return Unexpect(ErrCode::Value::ComponentTypeIndexOutOfBounds); |
603 | 7 | } |
604 | 411 | S.addCoreType(*Entry); |
605 | 411 | return {}; |
606 | 418 | } |
607 | 0 | default: |
608 | 0 | spdlog::error(ErrCode::Value::MalformedAliasTarget); |
609 | 0 | spdlog::error( |
610 | 0 | " Outer aliases may only target module, type, component."sv); |
611 | 0 | return Unexpect(ErrCode::Value::MalformedAliasTarget); |
612 | 492 | } |
613 | 492 | } |
614 | 4.25k | switch (Sort.getSortType()) { |
615 | 2.45k | case AST::Component::Sort::SortType::Type: { |
616 | 2.45k | const auto *Entry = Target->getType(Idx); |
617 | 2.45k | if (Entry == nullptr) { |
618 | 15 | spdlog::error(ErrCode::Value::ComponentTypeIndexOutOfBounds); |
619 | 15 | spdlog::error(" Aliased type index {} out of bounds."sv, Idx); |
620 | 15 | return Unexpect(ErrCode::Value::ComponentTypeIndexOutOfBounds); |
621 | 15 | } |
622 | | // A type crossing a component boundary must carry no free resource id. |
623 | 2.44k | const auto *Inside = CompCtx.scopeInsideTarget(Ct); |
624 | 2.44k | if (Inside != nullptr && |
625 | 508 | Inside->Kind == Component::ScopeKind::Component) { |
626 | 0 | std::unordered_set<uint32_t> Ids; |
627 | 0 | Component::ExternInfo Probe; |
628 | 0 | Probe.Kind = Component::ExternKind::TypeBound; |
629 | 0 | Probe.Type = *Entry; |
630 | 0 | CompTypes.collectResources(Probe, Ids); |
631 | | // Ids bound by the aliased type itself are not free. |
632 | 0 | const Component::Scope *Binder = nullptr; |
633 | 0 | if (Entry->Inst != nullptr) { |
634 | 0 | Binder = Entry->Inst->DeclScope; |
635 | 0 | } else if (Entry->Comp != nullptr) { |
636 | 0 | Binder = Entry->Comp->DeclScope; |
637 | 0 | } |
638 | 0 | bool HasFree = false; |
639 | 0 | for (const uint32_t Id : Ids) { |
640 | 0 | if (Binder == nullptr || !CompTypes.originatesIn(Id, *Binder)) { |
641 | 0 | HasFree = true; |
642 | 0 | break; |
643 | 0 | } |
644 | 0 | } |
645 | 0 | if (HasFree) { |
646 | 0 | spdlog::error(ErrCode::Value::ComponentAliasResourceLeak); |
647 | 0 | spdlog::error( |
648 | 0 | " Cannot alias an outer type which transitively refers to " |
649 | 0 | "resources not defined in the current component."sv); |
650 | 0 | return Unexpect(ErrCode::Value::ComponentAliasResourceLeak); |
651 | 0 | } |
652 | 0 | } |
653 | 2.44k | S.addType(*Entry); |
654 | 2.44k | return {}; |
655 | 2.44k | } |
656 | 1.79k | case AST::Component::Sort::SortType::Component: { |
657 | 1.79k | if (InTypeDecl) { |
658 | 2 | spdlog::error(ErrCode::Value::InvalidTypeReference); |
659 | 2 | spdlog::error(" Aliases in a component or instance type may only " |
660 | 2 | "refer to types or instances."sv); |
661 | 2 | return Unexpect(ErrCode::Value::InvalidTypeReference); |
662 | 2 | } |
663 | 1.79k | const auto *Comp = Target->getComponent(Idx); |
664 | 1.79k | if (Comp == nullptr) { |
665 | 17 | spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds); |
666 | 17 | spdlog::error(" Aliased component index {} out of bounds."sv, Idx); |
667 | 17 | return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds); |
668 | 17 | } |
669 | 1.77k | S.addComponent(Comp); |
670 | 1.77k | return {}; |
671 | 1.79k | } |
672 | 0 | default: |
673 | 0 | spdlog::error(ErrCode::Value::MalformedAliasTarget); |
674 | 0 | spdlog::error( |
675 | 0 | " Outer aliases may only target module, type, component."sv); |
676 | 0 | return Unexpect(ErrCode::Value::MalformedAliasTarget); |
677 | 4.25k | } |
678 | 4.25k | } |
679 | 0 | default: |
680 | 0 | spdlog::error(ErrCode::Value::MalformedAliasTarget); |
681 | 0 | return Unexpect(ErrCode::Value::MalformedAliasTarget); |
682 | 5.40k | } |
683 | 5.40k | } |
684 | | |
685 | | Expect<void> |
686 | 2.53M | Validator::validate(const AST::Component::TypeSection &TypeSec) noexcept { |
687 | 2.53M | for (const auto &Type : TypeSec.getContent()) { |
688 | 2.41M | EXPECTED_TRY(validate(Type).map_error([](auto E) { |
689 | 2.41M | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Type)); |
690 | 2.41M | return E; |
691 | 2.41M | })); |
692 | 2.41M | } |
693 | 2.53M | return {}; |
694 | 2.53M | } |
695 | | |
696 | | Expect<void> |
697 | 45.1k | Validator::validate(const AST::Component::CanonSection &CanonSec) noexcept { |
698 | 45.8k | for (const auto &Canon : CanonSec.getContent()) { |
699 | 45.8k | EXPECTED_TRY(validate(Canon).map_error([](auto E) { |
700 | 45.8k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Canon)); |
701 | 45.8k | return E; |
702 | 45.8k | })); |
703 | 45.8k | } |
704 | 44.6k | return {}; |
705 | 45.1k | } |
706 | | |
707 | | Expect<void> |
708 | 530 | Validator::validate(const AST::Component::StartSection &StartSec) noexcept { |
709 | 530 | const auto &Start = StartSec.getContent(); |
710 | 530 | auto &S = CompCtx.top(); |
711 | 530 | const auto *FI = S.getFunc(Start.getFunctionIndex()); |
712 | 530 | if (FI == nullptr || FI->FT == nullptr) { |
713 | 35 | spdlog::error(ErrCode::Value::ComponentFunctionIndexOutOfBounds); |
714 | 35 | spdlog::error(" Start function index {} out of bounds (size {})."sv, |
715 | 35 | Start.getFunctionIndex(), S.Funcs.size()); |
716 | 35 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Start)); |
717 | 35 | return Unexpect(ErrCode::Value::ComponentFunctionIndexOutOfBounds); |
718 | 35 | } |
719 | 495 | const auto Params = FI->FT->getParamList(); |
720 | 495 | const auto Args = Start.getArguments(); |
721 | 495 | if (Params.size() != Args.size()) { |
722 | 1 | spdlog::error(ErrCode::Value::ArgTypeMismatch); |
723 | 1 | spdlog::error(" Start takes {} arguments but the function has {} " |
724 | 1 | "parameters."sv, |
725 | 1 | Args.size(), Params.size()); |
726 | 1 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Start)); |
727 | 1 | return Unexpect(ErrCode::Value::ArgTypeMismatch); |
728 | 1 | } |
729 | 494 | for (size_t I = 0; I < Args.size(); ++I) { |
730 | 0 | const uint32_t ValIdx = Args[I]; |
731 | 0 | if (ValIdx >= S.Values.size()) { |
732 | 0 | spdlog::error(ErrCode::Value::InvalidIndex); |
733 | 0 | spdlog::error(" Start argument value index {} out of bounds."sv, |
734 | 0 | ValIdx); |
735 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Start)); |
736 | 0 | return Unexpect(ErrCode::Value::InvalidIndex); |
737 | 0 | } |
738 | 0 | EXPECTED_TRY(S.consumeValue(ValIdx).map_error([ValIdx](auto E) { |
739 | 0 | spdlog::error(" Start argument value {} was already consumed."sv, |
740 | 0 | ValIdx); |
741 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Start)); |
742 | 0 | return E; |
743 | 0 | })); |
744 | 0 | if (!Component::Matcher(CompTypes).matchValType( |
745 | 0 | S.Values[ValIdx].Type, |
746 | 0 | {Params[I].getValType(), FI->Home, FI->Remap})) { |
747 | 0 | spdlog::error(ErrCode::Value::ArgTypeMismatch); |
748 | 0 | spdlog::error(" Start argument {} type mismatch."sv, I); |
749 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Start)); |
750 | 0 | return Unexpect(ErrCode::Value::ArgTypeMismatch); |
751 | 0 | } |
752 | 0 | } |
753 | 494 | const auto Results = FI->FT->getResultList(); |
754 | 494 | if (Start.getResult() != Results.size()) { |
755 | 4 | spdlog::error(ErrCode::Value::ArgTypeMismatch); |
756 | 4 | spdlog::error(" Start declares {} results but the function has {}."sv, |
757 | 4 | Start.getResult(), Results.size()); |
758 | 4 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Start)); |
759 | 4 | return Unexpect(ErrCode::Value::ArgTypeMismatch); |
760 | 4 | } |
761 | 490 | for (const auto &R : Results) { |
762 | 353 | S.addValue({R.getValType(), FI->Home, FI->Remap}); |
763 | 353 | } |
764 | 490 | return {}; |
765 | 494 | } |
766 | | |
767 | | Expect<void> Validator::validate(const AST::Component::ImportSection &ImpSec, |
768 | 73.0k | Component::Shape &Out) noexcept { |
769 | 73.0k | for (const auto &Import : ImpSec.getContent()) { |
770 | 5.33k | EXPECTED_TRY(validate(Import, Out).map_error([](auto E) { |
771 | 5.33k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Import)); |
772 | 5.33k | return E; |
773 | 5.33k | })); |
774 | 5.33k | } |
775 | 72.5k | return {}; |
776 | 73.0k | } |
777 | | |
778 | | Expect<void> Validator::validate(const AST::Component::Import &Im, |
779 | 5.33k | Component::Shape &Out) noexcept { |
780 | | // The descriptor is checked before the import name. |
781 | 5.33k | Component::ExternInfo Resolved; |
782 | 5.33k | EXPECTED_TRY(validate(Im.getDesc(), true, Resolved)); |
783 | 5.19k | EXPECTED_TRY(auto Info, CompCtx.defineImport( |
784 | 4.88k | Im.getName(), Resolved, Im.getImplements(), |
785 | 4.88k | Im.getExternalIds(), Im.getVersionSuffixes())); |
786 | 4.88k | Out.Imports.emplace_back(std::string(Im.getName()), Info); |
787 | 4.88k | return {}; |
788 | 5.19k | } |
789 | | |
790 | | Expect<void> Validator::validate(const AST::Component::ExportSection &ExpSec, |
791 | 3.47k | Component::Shape &Out) noexcept { |
792 | 3.47k | for (const auto &Export : ExpSec.getContent()) { |
793 | 2.14k | EXPECTED_TRY(validate(Export, Out).map_error([](auto E) { |
794 | 2.14k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Export)); |
795 | 2.14k | return E; |
796 | 2.14k | })); |
797 | 2.14k | } |
798 | 2.77k | return {}; |
799 | 3.47k | } |
800 | | |
801 | | Expect<void> Validator::validate(const AST::Component::Export &Ex, |
802 | 2.14k | Component::Shape &Out) noexcept { |
803 | 2.14k | auto &S = CompCtx.top(); |
804 | | // Index bounds are diagnosed before the name; a valid name refines the code. |
805 | 2.14k | { |
806 | 2.14k | const auto &SI = Ex.getSortIndex(); |
807 | 2.14k | Component::ExternName CN; |
808 | 2.14k | const bool NameOk = CN.parse(Ex.getName()).has_value(); |
809 | 2.14k | const uint32_t Idx = SI.getIdx(); |
810 | 2.14k | bool OOB = false; |
811 | 2.14k | ErrCode::Value Code = ErrCode::Value::DefTypeIndexOutOfBounds; |
812 | 2.14k | if (SI.getSort().isCore()) { |
813 | 23 | if (SI.getSort().getCoreSortType() == |
814 | 23 | AST::Component::Sort::CoreSortType::Module && |
815 | 10 | S.getCoreModule(Idx) == nullptr) { |
816 | 10 | OOB = true; |
817 | 10 | if (NameOk) { |
818 | 1 | Code = ErrCode::Value::ComponentModuleIndexOutOfBounds; |
819 | 1 | } |
820 | 10 | } |
821 | 2.12k | } else { |
822 | 2.12k | switch (SI.getSort().getSortType()) { |
823 | 221 | case AST::Component::Sort::SortType::Func: |
824 | 221 | if (S.getFunc(Idx) == nullptr) { |
825 | 64 | OOB = true; |
826 | 64 | if (NameOk) { |
827 | 14 | Code = ErrCode::Value::ComponentFunctionIndexOutOfBounds; |
828 | 14 | } |
829 | 64 | } |
830 | 221 | break; |
831 | 247 | case AST::Component::Sort::SortType::Instance: |
832 | 247 | if (S.getInstance(Idx) == nullptr) { |
833 | 65 | OOB = true; |
834 | 65 | if (NameOk) { |
835 | 18 | Code = ErrCode::Value::ComponentInstanceIndexOutOfBounds; |
836 | 18 | } |
837 | 65 | } |
838 | 247 | break; |
839 | 1.65k | default: |
840 | 1.65k | break; |
841 | 2.12k | } |
842 | 2.12k | } |
843 | 2.14k | if (OOB) { |
844 | 139 | spdlog::error(Code); |
845 | 139 | spdlog::error(" Export index {} out of bounds."sv, Idx); |
846 | 139 | return Unexpect(Code); |
847 | 139 | } |
848 | 2.14k | } |
849 | 3.74k | EXPECTED_TRY(auto Inferred, CompCtx.resolveSortIndex(Ex.getSortIndex())); |
850 | | // Exporting a value consumes it. |
851 | 3.74k | if (!Ex.getSortIndex().getSort().isCore() && |
852 | 1.73k | Ex.getSortIndex().getSort().getSortType() == |
853 | 1.73k | AST::Component::Sort::SortType::Value) { |
854 | 131 | const uint32_t ValIdx = Ex.getSortIndex().getIdx(); |
855 | 131 | EXPECTED_TRY(S.consumeValue(ValIdx).map_error([ValIdx](auto E) { |
856 | 130 | spdlog::error(" Exported value {} was already consumed."sv, ValIdx); |
857 | 130 | return E; |
858 | 130 | })); |
859 | | // Exported values must not transitively contain borrow handles. |
860 | 130 | if (CompTypes.containsBorrow(S.Values[ValIdx].Type)) { |
861 | 0 | spdlog::error(ErrCode::Value::InvalidTypeReference); |
862 | 0 | spdlog::error(" Exported value types cannot contain borrows."sv); |
863 | 0 | return Unexpect(ErrCode::Value::InvalidTypeReference); |
864 | 0 | } |
865 | 130 | } |
866 | 3.21k | EXPECTED_TRY( |
867 | 3.21k | Component::ExternName CN, |
868 | 3.21k | CompCtx.registerExportName( |
869 | 3.21k | Ex.getName(), Inferred.Kind == Component::ExternKind::InstanceType, |
870 | 3.21k | Ex.getImplements(), Ex.getExternalIds(), Ex.getVersionSuffixes())); |
871 | 3.21k | std::optional<Component::ExternInfo> Ascribed; |
872 | 3.21k | if (Ex.getDesc().has_value()) { |
873 | 156 | EXPECTED_TRY(validate(*Ex.getDesc(), false, Ascribed.emplace())); |
874 | 156 | } |
875 | 1.47k | EXPECTED_TRY(auto Result, CompCtx.defineExport(CN, Inferred, Ascribed)); |
876 | | // The re-exported value index is born consumed. |
877 | 1.43k | if (Result.Kind == Component::ExternKind::ValueBound) { |
878 | 20 | CompCtx.top().Values.back().Consumed = true; |
879 | 20 | } |
880 | 1.43k | Out.Exports.emplace(std::string(Ex.getName()), Result); |
881 | 1.43k | return {}; |
882 | 1.47k | } |
883 | | |
884 | | Expect<void> |
885 | 13.2k | Validator::validate(const AST::Component::ValueSection &ValSec) noexcept { |
886 | 13.8k | for (const auto &Value : ValSec.getContent()) { |
887 | 13.8k | EXPECTED_TRY(validate(Value.getType()).map_error([](auto E) { |
888 | 13.7k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Value)); |
889 | 13.7k | return E; |
890 | 13.7k | })); |
891 | 13.7k | Component::ValueDecoder Decoder(Value.getData(), CompCtx.top()); |
892 | 13.7k | auto Decoded = Decoder.decode(Value.getType()); |
893 | 13.7k | if (!Decoded) { |
894 | 792 | spdlog::error(ErrCode::Value::ComponentMalformedValue); |
895 | 792 | spdlog::error(" Value definition payload does not match its type."sv); |
896 | 792 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Comp_Sec_Value)); |
897 | 792 | return Unexpect(ErrCode::Value::ComponentMalformedValue); |
898 | 792 | } |
899 | 12.9k | Value.setDecoded(std::move(*Decoded)); |
900 | 12.9k | CompCtx.top().addValue({Value.getType(), &CompCtx.top(), nullptr}); |
901 | 12.9k | } |
902 | 12.4k | return {}; |
903 | 13.2k | } |
904 | | |
905 | | } // namespace Validator |
906 | | } // namespace WasmEdge |