/src/WasmEdge/lib/validator/validator.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
3 | | |
4 | | #include "validator/validator.h" |
5 | | |
6 | | #include "ast/section.h" |
7 | | #include "common/errinfo.h" |
8 | | #include "common/hash.h" |
9 | | |
10 | | #include <numeric> |
11 | | #include <string> |
12 | | #include <unordered_set> |
13 | | |
14 | | using namespace std::literals; |
15 | | |
16 | | namespace WasmEdge { |
17 | | namespace Validator { |
18 | | |
19 | | namespace { |
20 | | |
21 | | // One-shot builder for the pre-defined core function SubTypes. |
22 | | AST::SubType makeCoreFuncType(std::initializer_list<TypeCode> Params, |
23 | 17.1k | std::initializer_list<TypeCode> Results) { |
24 | 17.1k | AST::FunctionType FT; |
25 | 17.1k | for (auto T : Params) { |
26 | 17.1k | FT.getParamTypes().emplace_back(T); |
27 | 17.1k | } |
28 | 17.1k | for (auto T : Results) { |
29 | 8.55k | FT.getReturnTypes().emplace_back(T); |
30 | 8.55k | } |
31 | 17.1k | AST::SubType ST; |
32 | 17.1k | ST.getCompositeType().setFunctionType(std::move(FT)); |
33 | 17.1k | return ST; |
34 | 17.1k | } |
35 | | |
36 | | static constexpr uint32_t MaxSubtypeDepth = 63; |
37 | | static constexpr uint32_t Unvisited = UINT32_MAX; |
38 | | static constexpr uint32_t Visiting = UINT32_MAX - 1; |
39 | | |
40 | | // TODO: make the super type depth table instead of recursively querying. |
41 | | Expect<uint32_t> |
42 | | checkSubtypeDepth(const uint32_t BaseIdx, uint32_t TestIdx, uint32_t Depth, |
43 | | std::vector<uint32_t> &DepthMap, |
44 | 27 | const std::vector<const WasmEdge::AST::SubType *> &TypeVec) { |
45 | 27 | if (TestIdx >= DepthMap.size()) { |
46 | 0 | DepthMap.resize(TestIdx + 1, Unvisited); |
47 | 27 | } else if (DepthMap[TestIdx] == Visiting) { |
48 | 0 | spdlog::error(ErrCode::Value::InvalidSubType); |
49 | 0 | spdlog::error(" Cycle detected in subtype hierarchy for type {}."sv, |
50 | 0 | BaseIdx); |
51 | 0 | return Unexpect(ErrCode::Value::InvalidSubType); |
52 | 27 | } else if (DepthMap[TestIdx] != Unvisited) { |
53 | 0 | return DepthMap[TestIdx]; |
54 | 0 | } |
55 | | |
56 | 27 | if (Depth >= MaxSubtypeDepth) { |
57 | 0 | spdlog::error(ErrCode::Value::InvalidSubType); |
58 | 0 | spdlog::error(" Subtype depth for type {} exceeded the limits of {}"sv, |
59 | 0 | BaseIdx, MaxSubtypeDepth); |
60 | 0 | return Unexpect(ErrCode::Value::InvalidSubType); |
61 | 0 | } |
62 | | |
63 | 27 | DepthMap[TestIdx] = Visiting; |
64 | 27 | uint32_t MaxDepth = 0; |
65 | 27 | const auto &TestType = *TypeVec[TestIdx]; |
66 | 27 | for (const auto SuperIdx : TestType.getSuperTypeIndices()) { |
67 | 0 | if (unlikely(SuperIdx >= TypeVec.size())) { |
68 | 0 | spdlog::error(ErrCode::Value::InvalidSubType); |
69 | 0 | spdlog::error(ErrInfo::InfoForbidIndex( |
70 | 0 | ErrInfo::IndexCategory::DefinedType, SuperIdx, |
71 | 0 | static_cast<uint32_t>(TypeVec.size()))); |
72 | 0 | return Unexpect(ErrCode::Value::InvalidSubType); |
73 | 0 | } |
74 | 0 | EXPECTED_TRY( |
75 | 0 | auto RetDepth, |
76 | 0 | checkSubtypeDepth(BaseIdx, SuperIdx, Depth + 1, DepthMap, TypeVec) |
77 | 0 | .map_error([=](auto E) { |
78 | 0 | spdlog::error( |
79 | 0 | " When checking subtype hierarchy of super type {}."sv, |
80 | 0 | SuperIdx); |
81 | 0 | return E; |
82 | 0 | })); |
83 | 0 | MaxDepth = std::max(MaxDepth, RetDepth + 1); |
84 | 0 | } |
85 | | |
86 | 27 | if (MaxDepth >= MaxSubtypeDepth) { |
87 | 0 | spdlog::error(ErrCode::Value::InvalidSubType); |
88 | 0 | spdlog::error(" Subtype depth for type {} exceeded the limits of {}"sv, |
89 | 0 | BaseIdx, MaxSubtypeDepth); |
90 | 0 | return Unexpect(ErrCode::Value::InvalidSubType); |
91 | 0 | } |
92 | | |
93 | 27 | DepthMap[TestIdx] = MaxDepth; |
94 | 27 | return MaxDepth; |
95 | 27 | } |
96 | | |
97 | | } // namespace |
98 | | |
99 | | // Validator constructor. See "include/validator/validator.h". |
100 | | Validator::Validator(const Configure &Conf) noexcept |
101 | 8.55k | : Conf(Conf), |
102 | 8.55k | CoreFuncType_I32_I32(makeCoreFuncType({TypeCode::I32}, {TypeCode::I32})), |
103 | 8.55k | CoreFuncType_I32_Void(makeCoreFuncType({TypeCode::I32}, {})) {} |
104 | | |
105 | | // Validate Module. See "include/validator/validator.h". |
106 | 6.05k | Expect<void> Validator::validate(const AST::Module &Mod) { |
107 | | // https://webassembly.github.io/spec/core/valid/modules.html |
108 | 6.05k | Checker.reset(true); |
109 | | |
110 | | // Validate and register type section. |
111 | 6.05k | EXPECTED_TRY(validate(Mod.getTypeSection()).map_error([](auto E) { |
112 | 5.99k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Type)); |
113 | 5.99k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
114 | 5.99k | return E; |
115 | 5.99k | })); |
116 | | |
117 | | // Validate and register the import section in FormChecker. |
118 | 5.99k | EXPECTED_TRY(validate(Mod.getImportSection()).map_error([](auto E) { |
119 | 5.97k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Import)); |
120 | 5.97k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
121 | 5.97k | return E; |
122 | 5.97k | })); |
123 | | |
124 | | // Validate the function section and register functions in FormChecker. |
125 | 5.97k | EXPECTED_TRY(validate(Mod.getFunctionSection()).map_error([](auto E) { |
126 | 5.95k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Function)); |
127 | 5.95k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
128 | 5.95k | return E; |
129 | 5.95k | })); |
130 | | |
131 | | // Validate the table section and register tables in FormChecker. |
132 | 5.95k | EXPECTED_TRY(validate(Mod.getTableSection()).map_error([](auto E) { |
133 | 5.71k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Table)); |
134 | 5.71k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
135 | 5.71k | return E; |
136 | 5.71k | })); |
137 | | |
138 | | // Validate the memory section and register memories in FormChecker. |
139 | 5.71k | EXPECTED_TRY(validate(Mod.getMemorySection()).map_error([](auto E) { |
140 | 5.53k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Memory)); |
141 | 5.53k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
142 | 5.53k | return E; |
143 | 5.53k | })); |
144 | | |
145 | | // Validate the global section and register globals in FormChecker. |
146 | 5.53k | EXPECTED_TRY(validate(Mod.getGlobalSection()).map_error([](auto E) { |
147 | 5.22k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Global)); |
148 | 5.22k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
149 | 5.22k | return E; |
150 | 5.22k | })); |
151 | | |
152 | | // Validate the tag section and register tags in FormChecker. |
153 | 5.22k | EXPECTED_TRY(validate(Mod.getTagSection()).map_error([](auto E) { |
154 | 5.17k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Tag)); |
155 | 5.17k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
156 | 5.17k | return E; |
157 | 5.17k | })); |
158 | | |
159 | | // Validate export section. |
160 | 5.17k | EXPECTED_TRY(validate(Mod.getExportSection()).map_error([](auto E) { |
161 | 5.05k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Export)); |
162 | 5.05k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
163 | 5.05k | return E; |
164 | 5.05k | })); |
165 | | |
166 | | // Validate start section. |
167 | 5.05k | EXPECTED_TRY(validate(Mod.getStartSection()).map_error([](auto E) { |
168 | 5.00k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Start)); |
169 | 5.00k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
170 | 5.00k | return E; |
171 | 5.00k | })); |
172 | | |
173 | | // Validate the element section that initializes tables. |
174 | 5.00k | EXPECTED_TRY(validate(Mod.getElementSection()).map_error([](auto E) { |
175 | 4.87k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Element)); |
176 | 4.87k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
177 | 4.87k | return E; |
178 | 4.87k | })); |
179 | | |
180 | | // Validate the data section that initializes memories. |
181 | 4.87k | EXPECTED_TRY(validate(Mod.getDataSection()).map_error([](auto E) { |
182 | 4.85k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Data)); |
183 | 4.85k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
184 | 4.85k | return E; |
185 | 4.85k | })); |
186 | | |
187 | | // Validate code section and expressions. |
188 | 4.85k | EXPECTED_TRY(validate(Mod.getCodeSection()).map_error([](auto E) { |
189 | 3.35k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Sec_Code)); |
190 | 3.35k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
191 | 3.35k | return E; |
192 | 3.35k | })); |
193 | | |
194 | | // Multiple tables are for the ReferenceTypes proposal. |
195 | 3.35k | if (Checker.getTables().size() > 1 && |
196 | 50 | !Conf.hasProposal(Proposal::ReferenceTypes)) { |
197 | 0 | spdlog::error(ErrCode::Value::MultiTables); |
198 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::ReferenceTypes)); |
199 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
200 | 0 | return Unexpect(ErrCode::Value::MultiTables); |
201 | 0 | } |
202 | | |
203 | | // Multiple memories are for the MultiMemories proposal. |
204 | 3.35k | if (Checker.getMemories().size() > 1 && |
205 | 82 | !Conf.hasProposal(Proposal::MultiMemories)) { |
206 | 0 | spdlog::error(ErrCode::Value::MultiMemories); |
207 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::MultiMemories)); |
208 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
209 | 0 | return Unexpect(ErrCode::Value::MultiMemories); |
210 | 0 | } |
211 | | |
212 | | // Set the validated flag. |
213 | 3.35k | const_cast<AST::Module &>(Mod).setIsValidated(); |
214 | 3.35k | return {}; |
215 | 3.35k | } |
216 | | |
217 | | // Validate Sub type. See "include/validator/validator.h". |
218 | | Expect<void> Validator::validate(const AST::SubType &Type, uint32_t OwnTypeIdx, |
219 | 7.45k | std::vector<uint32_t> &SubTypeDepthMap) { |
220 | 7.45k | const auto &TypeVec = Checker.getTypes(); |
221 | 7.45k | const auto &CompType = Type.getCompositeType(); |
222 | | |
223 | | // Check the validation of the composite type. |
224 | 7.45k | if (CompType.isFunc()) { |
225 | 7.08k | const auto &FType = CompType.getFuncType(); |
226 | 7.08k | for (auto &PType : FType.getParamTypes()) { |
227 | 6.49k | EXPECTED_TRY(Checker.validate(PType).map_error([](auto E) { |
228 | 6.49k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Function)); |
229 | 6.49k | return E; |
230 | 6.49k | })); |
231 | 6.49k | } |
232 | 7.08k | if (unlikely(!Conf.hasProposal(Proposal::MultiValue)) && |
233 | 0 | FType.getReturnTypes().size() > 1) { |
234 | 0 | spdlog::error(ErrCode::Value::InvalidResultArity); |
235 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::MultiValue)); |
236 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Function)); |
237 | 0 | return Unexpect(ErrCode::Value::InvalidResultArity); |
238 | 0 | } |
239 | 7.08k | for (auto &RType : FType.getReturnTypes()) { |
240 | 5.30k | EXPECTED_TRY(Checker.validate(RType).map_error([](auto E) { |
241 | 5.30k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Function)); |
242 | 5.30k | return E; |
243 | 5.30k | })); |
244 | 5.30k | } |
245 | 7.08k | } else { |
246 | 367 | const auto &FTypes = CompType.getFieldTypes(); |
247 | 367 | for (auto &FieldType : FTypes) { |
248 | 227 | EXPECTED_TRY(Checker.validate(FieldType.getStorageType())); |
249 | 227 | } |
250 | 367 | } |
251 | | |
252 | | // In the current version, the length of the type index vector will be <= 1. |
253 | 7.43k | if (Type.getSuperTypeIndices().size() > 1) { |
254 | 2 | spdlog::error(ErrCode::Value::InvalidSubType); |
255 | 2 | spdlog::error(" Accepts only one super type currently."sv); |
256 | 2 | return Unexpect(ErrCode::Value::InvalidSubType); |
257 | 2 | } |
258 | | |
259 | 7.43k | for (const auto &Index : Type.getSuperTypeIndices()) { |
260 | | // A super type must be previously defined (smaller index than this sub |
261 | | // type), so OwnTypeIdx is the exclusive bound, subsuming the range check. |
262 | 36 | if (unlikely(Index >= OwnTypeIdx)) { |
263 | 9 | spdlog::error(ErrCode::Value::InvalidSubType); |
264 | 9 | spdlog::error(" Super type index {} must be smaller than the sub type " |
265 | 9 | "index {}."sv, |
266 | 9 | Index, OwnTypeIdx); |
267 | 9 | return Unexpect(ErrCode::Value::InvalidSubType); |
268 | 9 | } |
269 | | |
270 | 27 | EXPECTED_TRY( |
271 | 27 | checkSubtypeDepth(Index, Index, 0, SubTypeDepthMap, TypeVec) |
272 | 27 | .map_error([=](auto E) { |
273 | 27 | spdlog::error( |
274 | 27 | " When checking subtype hierarchy of super type {}."sv, |
275 | 27 | Index); |
276 | 27 | return E; |
277 | 27 | })); |
278 | | |
279 | 27 | if (TypeVec[Index]->isFinal()) { |
280 | 1 | spdlog::error(ErrCode::Value::InvalidSubType); |
281 | 1 | spdlog::error(" Super type should not be final."sv); |
282 | 1 | return Unexpect(ErrCode::Value::InvalidSubType); |
283 | 1 | } |
284 | 26 | auto &SuperType = TypeVec[Index]->getCompositeType(); |
285 | 26 | if (!AST::TypeMatcher::matchType(Checker.getTypes(), SuperType, CompType)) { |
286 | 20 | spdlog::error(ErrCode::Value::InvalidSubType); |
287 | 20 | spdlog::error(" Super type not matched."sv); |
288 | 20 | return Unexpect(ErrCode::Value::InvalidSubType); |
289 | 20 | } |
290 | 26 | } |
291 | 7.40k | return {}; |
292 | 7.43k | } |
293 | | |
294 | | // Validate Limit type. See "include/validator/validator.h". |
295 | 2.74k | Expect<void> Validator::validate(const AST::Limit &Lim) { |
296 | 2.74k | if (Lim.hasMax() && Lim.getMin() > Lim.getMax()) { |
297 | 109 | spdlog::error(ErrCode::Value::InvalidLimit); |
298 | 109 | spdlog::error(ErrInfo::InfoLimit(Lim.hasMax(), Lim.getMin(), Lim.getMax())); |
299 | 109 | return Unexpect(ErrCode::Value::InvalidLimit); |
300 | 109 | } |
301 | 2.63k | if (Lim.isShared() && unlikely(!Lim.hasMax())) { |
302 | 0 | spdlog::error(ErrCode::Value::SharedMemoryNoMax); |
303 | 0 | return Unexpect(ErrCode::Value::SharedMemoryNoMax); |
304 | 0 | } |
305 | 2.63k | return {}; |
306 | 2.63k | } |
307 | | |
308 | | // Validate Table type. See "include/validator/validator.h". |
309 | 871 | Expect<void> Validator::validate(const AST::TableType &Tab) { |
310 | | // Validate value type. |
311 | 871 | EXPECTED_TRY(Checker.validate(Tab.getRefType())); |
312 | | // Validate table limits. |
313 | 818 | const auto &Lim = Tab.getLimit(); |
314 | 818 | EXPECTED_TRY(validate(Lim).map_error([](auto E) { |
315 | 758 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Limit)); |
316 | 758 | return E; |
317 | 758 | })); |
318 | 758 | uint64_t Range = getMaxAddress(Lim.getAddrType()); |
319 | 758 | if (Lim.getMin() > Range || (Lim.hasMax() && Lim.getMax() > Range)) { |
320 | | // Since spec test has no related error message, use this error instead. |
321 | 123 | auto Code = Conf.hasProposal(Proposal::Memory64) |
322 | 123 | ? ErrCode::Value::InvalidTableSize64 |
323 | 123 | : ErrCode::Value::InvalidLimit; |
324 | 123 | spdlog::error(Code); |
325 | 123 | spdlog::error(ErrInfo::InfoLimit(Lim.hasMax(), Lim.getMin(), Lim.getMax())); |
326 | 123 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Limit)); |
327 | 123 | return Unexpect(Code); |
328 | 123 | } |
329 | 635 | return {}; |
330 | 758 | } |
331 | | |
332 | | // Validate Memory type. See "include/validator/validator.h". |
333 | 1.92k | Expect<void> Validator::validate(const AST::MemoryType &Mem) { |
334 | | // Validate memory limits. |
335 | 1.92k | const auto &Lim = Mem.getLimit(); |
336 | 1.92k | EXPECTED_TRY(validate(Lim).map_error([](auto E) { |
337 | 1.87k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Limit)); |
338 | 1.87k | return E; |
339 | 1.87k | })); |
340 | 1.87k | if (!Conf.hasProposal(Proposal::Memory64) && Lim.is64()) { |
341 | 0 | spdlog::error(ErrCode::Value::InvalidLimit); |
342 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::Memory64)); |
343 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Limit)); |
344 | 0 | return Unexpect(ErrCode::Value::InvalidLimit); |
345 | 0 | } |
346 | 1.87k | uint64_t Range = Lim.is32() ? (static_cast<uint64_t>(1) << 16) |
347 | 1.87k | : (static_cast<uint64_t>(1) << 48); |
348 | 1.87k | if (Lim.getMin() > Range || (Lim.hasMax() && Lim.getMax() > Range)) { |
349 | 142 | auto Code = Conf.hasProposal(Proposal::Memory64) |
350 | 142 | ? ErrCode::Value::InvalidMemPages64 |
351 | 142 | : ErrCode::Value::InvalidMemPages; |
352 | 142 | spdlog::error(Code); |
353 | 142 | spdlog::error(ErrInfo::InfoLimit(Lim.hasMax(), Lim.getMin(), Lim.getMax())); |
354 | 142 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Limit)); |
355 | 142 | return Unexpect(Code); |
356 | 142 | } |
357 | 1.73k | return {}; |
358 | 1.87k | } |
359 | | |
360 | | // Validate Global type. See "include/validator/validator.h". |
361 | 352 | Expect<void> Validator::validate(const AST::GlobalType &Glob) { |
362 | | // Validate value type. |
363 | 352 | return Checker.validate(Glob.getValType()); |
364 | 352 | } |
365 | | |
366 | | // Validate Table segment. See "include/validator/validator.h". |
367 | 821 | Expect<void> Validator::validate(const AST::TableSegment &TabSeg) { |
368 | 821 | if (TabSeg.getExpr().getInstrs().size() > 0) { |
369 | | // Check ref initialization is a const expression. |
370 | 5 | EXPECTED_TRY( |
371 | 5 | validateConstExpr(TabSeg.getExpr().getInstrs(), |
372 | 5 | {ValType(TabSeg.getTableType().getRefType())}) |
373 | 5 | .map_error([](auto E) { |
374 | 5 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Expression)); |
375 | 5 | return E; |
376 | 5 | })); |
377 | 816 | } else { |
378 | | // No init expression. Check that the reference type is nullable. |
379 | 816 | if (!TabSeg.getTableType().getRefType().isNullableRefType()) { |
380 | 4 | spdlog::error(ErrCode::Value::TypeCheckFailed); |
381 | 4 | spdlog::error(ErrInfo::InfoMismatch( |
382 | 4 | ValType(TypeCode::RefNull, |
383 | 4 | TabSeg.getTableType().getRefType().getHeapTypeCode(), |
384 | 4 | TabSeg.getTableType().getRefType().getTypeIndex()), |
385 | 4 | TabSeg.getTableType().getRefType())); |
386 | 4 | return Unexpect(ErrCode::Value::TypeCheckFailed); |
387 | 4 | } |
388 | 816 | } |
389 | | // Validate table type. |
390 | 815 | return validate(TabSeg.getTableType()).map_error([](auto E) { |
391 | 234 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Table)); |
392 | 234 | return E; |
393 | 234 | }); |
394 | 821 | } |
395 | | |
396 | | // Validate Global segment. See "include/validator/validator.h". |
397 | 608 | Expect<void> Validator::validate(const AST::GlobalSegment &GlobSeg) { |
398 | | // Check global initialization is a const expression. |
399 | 608 | EXPECTED_TRY(validateConstExpr(GlobSeg.getExpr().getInstrs(), |
400 | 297 | {GlobSeg.getGlobalType().getValType()}) |
401 | 297 | .map_error([](auto E) { |
402 | 297 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Expression)); |
403 | 297 | return E; |
404 | 297 | })); |
405 | | // Validate global type. |
406 | 297 | return validate(GlobSeg.getGlobalType()).map_error([](auto E) { |
407 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Global)); |
408 | 0 | return E; |
409 | 0 | }); |
410 | 608 | } |
411 | | |
412 | | // Validate Element segment. See "include/validator/validator.h". |
413 | 631 | Expect<void> Validator::validate(const AST::ElementSegment &ElemSeg) { |
414 | | // Check that initialization expressions are const expressions. |
415 | 1.40k | for (auto &Expr : ElemSeg.getInitExprs()) { |
416 | 1.40k | EXPECTED_TRY( |
417 | 1.40k | validateConstExpr(Expr.getInstrs(), {ValType(ElemSeg.getRefType())}) |
418 | 1.40k | .map_error([](auto E) { |
419 | 1.40k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Expression)); |
420 | 1.40k | return E; |
421 | 1.40k | })); |
422 | 1.40k | } |
423 | | |
424 | | // The reference type should be valid. |
425 | 549 | EXPECTED_TRY(Checker.validate(ElemSeg.getRefType())); |
426 | | |
427 | | // Passive and declarative cases are valid with a valid reference type. |
428 | 546 | if (ElemSeg.getMode() == AST::ElementSegment::ElemMode::Active) { |
429 | | // Check table index and reference type in context. |
430 | 311 | const auto &TableVec = Checker.getTables(); |
431 | 311 | if (ElemSeg.getIdx() >= TableVec.size()) { |
432 | 30 | spdlog::error(ErrCode::Value::InvalidTableIdx); |
433 | 30 | spdlog::error(ErrInfo::InfoForbidIndex( |
434 | 30 | ErrInfo::IndexCategory::Table, ElemSeg.getIdx(), |
435 | 30 | static_cast<uint32_t>(TableVec.size()))); |
436 | 30 | return Unexpect(ErrCode::Value::InvalidTableIdx); |
437 | 30 | } |
438 | 281 | if (!AST::TypeMatcher::matchType(Checker.getTypes(), |
439 | 281 | TableVec[ElemSeg.getIdx()].second, |
440 | 281 | ElemSeg.getRefType())) { |
441 | | // Reference type does not match. |
442 | 8 | spdlog::error(ErrCode::Value::TypeCheckFailed); |
443 | 8 | spdlog::error(ErrInfo::InfoMismatch(TableVec[ElemSeg.getIdx()].second, |
444 | 8 | ElemSeg.getRefType())); |
445 | 8 | return Unexpect(ErrCode::Value::TypeCheckFailed); |
446 | 8 | } |
447 | | // Check table initialization is a const expression. |
448 | 273 | return validateConstExpr(ElemSeg.getExpr().getInstrs(), |
449 | 273 | {ValType(TableVec[ElemSeg.getIdx()].first)}) |
450 | 273 | .map_error([](auto E) { |
451 | 4 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Expression)); |
452 | 4 | return E; |
453 | 4 | }); |
454 | 281 | } |
455 | 235 | return {}; |
456 | 546 | } |
457 | | |
458 | | // Validate Code segment. See "include/validator/validator.h". |
459 | | Expect<void> Validator::validate(const AST::CodeSegment &CodeSeg, |
460 | 12.8k | const uint32_t TypeIdx) { |
461 | | // Due to validation of the function section, the type at this index must |
462 | | // be a function type. |
463 | 12.8k | const auto &FuncType = |
464 | 12.8k | Checker.getTypes()[TypeIdx]->getCompositeType().getFuncType(); |
465 | | // Reset stack in FormChecker. |
466 | 12.8k | Checker.reset(); |
467 | | // Add parameters to this frame. |
468 | 12.8k | for (auto &Type : FuncType.getParamTypes()) { |
469 | | // Local passed by function parameters must have been initialized. |
470 | 10.5k | Checker.addLocal(Type, true); |
471 | 10.5k | } |
472 | | // Add locals to this frame. |
473 | 12.8k | for (auto Val : CodeSeg.getLocals()) { |
474 | 138M | for (uint32_t Cnt = 0; Cnt < Val.first; ++Cnt) { |
475 | | // The local value type should be valid. |
476 | 138M | EXPECTED_TRY(Checker.validate(Val.second)); |
477 | 138M | Checker.addLocal(Val.second, false); |
478 | 138M | } |
479 | 2.20k | } |
480 | | // Validate function body expression. |
481 | 12.8k | return Checker |
482 | 12.8k | .validate(CodeSeg.getExpr().getInstrs(), FuncType.getReturnTypes()) |
483 | 12.8k | .map_error([](auto E) { |
484 | 1.49k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Expression)); |
485 | 1.49k | return E; |
486 | 1.49k | }); |
487 | 12.8k | } |
488 | | |
489 | | // Validate Data segment. See "include/validator/validator.h". |
490 | 362 | Expect<void> Validator::validate(const AST::DataSegment &DataSeg) { |
491 | 362 | switch (DataSeg.getMode()) { |
492 | 203 | case AST::DataSegment::DataMode::Active: { |
493 | | // Check memory index in context. |
494 | 203 | const auto &MemVec = Checker.getMemories(); |
495 | 203 | if (DataSeg.getIdx() >= MemVec.size()) { |
496 | 19 | spdlog::error(ErrCode::Value::InvalidMemoryIdx); |
497 | 19 | spdlog::error(ErrInfo::InfoForbidIndex( |
498 | 19 | ErrInfo::IndexCategory::Memory, DataSeg.getIdx(), |
499 | 19 | static_cast<uint32_t>(MemVec.size()))); |
500 | 19 | return Unexpect(ErrCode::Value::InvalidMemoryIdx); |
501 | 19 | } |
502 | | // Check memory initialization is a const expression. |
503 | 184 | return validateConstExpr(DataSeg.getExpr().getInstrs(), |
504 | 184 | {ValType(MemVec[DataSeg.getIdx()])}) |
505 | 184 | .map_error([](auto E) { |
506 | 5 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Expression)); |
507 | 5 | return E; |
508 | 5 | }); |
509 | 203 | } |
510 | 159 | case AST::DataSegment::DataMode::Passive: |
511 | | // Passive case is always valid. |
512 | 159 | return {}; |
513 | 0 | default: |
514 | 0 | return {}; |
515 | 362 | } |
516 | 362 | } |
517 | | |
518 | | // Validate Import description. See "include/validator/validator.h". |
519 | 565 | Expect<void> Validator::validate(const AST::ImportDesc &ImpDesc) { |
520 | 565 | switch (ImpDesc.getExternalType()) { |
521 | | // External type and external content are ensured to match in the loader |
522 | | // phase. |
523 | 376 | case ExternalType::Function: { |
524 | 376 | const auto TId = ImpDesc.getExternalFuncTypeIdx(); |
525 | | // Function type index must exist in context and be valid. |
526 | 376 | if (TId >= Checker.getTypes().size()) { |
527 | 10 | spdlog::error(ErrCode::Value::InvalidFuncTypeIdx); |
528 | 10 | spdlog::error(ErrInfo::InfoForbidIndex( |
529 | 10 | ErrInfo::IndexCategory::FunctionType, TId, |
530 | 10 | static_cast<uint32_t>(Checker.getTypes().size()))); |
531 | 10 | return Unexpect(ErrCode::Value::InvalidFuncTypeIdx); |
532 | 10 | } |
533 | 366 | if (!Checker.getTypes()[TId]->getCompositeType().isFunc()) { |
534 | 1 | spdlog::error(ErrCode::Value::InvalidFuncTypeIdx); |
535 | 1 | spdlog::error(" Defined type index {} is not a function type."sv, TId); |
536 | 1 | return Unexpect(ErrCode::Value::InvalidFuncTypeIdx); |
537 | 1 | } |
538 | 365 | Checker.addRef(static_cast<uint32_t>(Checker.getFunctions().size())); |
539 | 365 | Checker.addFunc(TId, true); |
540 | 365 | return {}; |
541 | 366 | } |
542 | 56 | case ExternalType::Table: { |
543 | 56 | const auto &TabType = ImpDesc.getExternalTableType(); |
544 | | // Table type must be valid. |
545 | 56 | EXPECTED_TRY(validate(TabType).map_error([](auto E) { |
546 | 54 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Table)); |
547 | 54 | return E; |
548 | 54 | })); |
549 | 54 | Checker.addTable(TabType); |
550 | 54 | return {}; |
551 | 56 | } |
552 | 56 | case ExternalType::Memory: { |
553 | 56 | const auto &MemType = ImpDesc.getExternalMemoryType(); |
554 | | // Memory type must be valid. |
555 | 56 | EXPECTED_TRY(validate(MemType).map_error([](auto E) { |
556 | 53 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Memory)); |
557 | 53 | return E; |
558 | 53 | })); |
559 | 53 | Checker.addMemory(MemType); |
560 | 53 | return {}; |
561 | 56 | } |
562 | 22 | case ExternalType::Tag: { |
563 | 22 | const auto &T = ImpDesc.getExternalTagType(); |
564 | | // Tag type index must exist in context. |
565 | 22 | auto TagTypeIdx = T.getTypeIdx(); |
566 | 22 | if (TagTypeIdx >= Checker.getTypes().size()) { |
567 | 7 | spdlog::error(ErrCode::Value::InvalidTagIdx); |
568 | 7 | spdlog::error(ErrInfo::InfoForbidIndex( |
569 | 7 | ErrInfo::IndexCategory::TagType, TagTypeIdx, |
570 | 7 | static_cast<uint32_t>(Checker.getTypes().size()))); |
571 | 7 | return Unexpect(ErrCode::Value::InvalidTagIdx); |
572 | 7 | } |
573 | | // Tag type must be valid. |
574 | 15 | auto &CompType = Checker.getTypes()[TagTypeIdx]->getCompositeType(); |
575 | 15 | if (!CompType.isFunc()) { |
576 | 1 | spdlog::error(ErrCode::Value::InvalidTagIdx); |
577 | 1 | spdlog::error(" Defined type index {} is not a function type."sv, |
578 | 1 | TagTypeIdx); |
579 | 1 | return Unexpect(ErrCode::Value::InvalidTagIdx); |
580 | 1 | } |
581 | 14 | if (!CompType.getFuncType().getReturnTypes().empty()) { |
582 | 1 | spdlog::error(ErrCode::Value::InvalidTagResultType); |
583 | 1 | return Unexpect(ErrCode::Value::InvalidTagResultType); |
584 | 1 | } |
585 | 13 | Checker.addTag(TagTypeIdx); |
586 | 13 | return {}; |
587 | 14 | } |
588 | 55 | case ExternalType::Global: { |
589 | 55 | const auto &GlobType = ImpDesc.getExternalGlobalType(); |
590 | | // Global type must be valid. |
591 | 55 | EXPECTED_TRY(validate(GlobType).map_error([](auto E) { |
592 | 52 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Global)); |
593 | 52 | return E; |
594 | 52 | })); |
595 | 52 | Checker.addGlobal(GlobType, true); |
596 | 52 | return {}; |
597 | 55 | } |
598 | 0 | default: |
599 | 0 | return {}; |
600 | 565 | } |
601 | 565 | } |
602 | | |
603 | | // Validate Export description. See "include/validator/validator.h". |
604 | 10.3k | Expect<void> Validator::validate(const AST::ExportDesc &ExpDesc) { |
605 | 10.3k | auto Id = ExpDesc.getExternalIndex(); |
606 | 10.3k | switch (ExpDesc.getExternalType()) { |
607 | 9.96k | case ExternalType::Function: |
608 | 9.96k | if (Id >= Checker.getFunctions().size()) { |
609 | 33 | spdlog::error(ErrCode::Value::InvalidFuncIdx); |
610 | 33 | spdlog::error(ErrInfo::InfoForbidIndex( |
611 | 33 | ErrInfo::IndexCategory::Function, Id, |
612 | 33 | static_cast<uint32_t>(Checker.getFunctions().size()))); |
613 | 33 | return Unexpect(ErrCode::Value::InvalidFuncIdx); |
614 | 33 | } |
615 | 9.93k | Checker.addRef(Id); |
616 | 9.93k | return {}; |
617 | 39 | case ExternalType::Table: |
618 | 39 | if (Id >= Checker.getTables().size()) { |
619 | 12 | spdlog::error(ErrCode::Value::InvalidTableIdx); |
620 | 12 | spdlog::error(ErrInfo::InfoForbidIndex( |
621 | 12 | ErrInfo::IndexCategory::Table, Id, |
622 | 12 | static_cast<uint32_t>(Checker.getTables().size()))); |
623 | 12 | return Unexpect(ErrCode::Value::InvalidTableIdx); |
624 | 12 | } |
625 | 27 | return {}; |
626 | 123 | case ExternalType::Memory: |
627 | 123 | if (Id >= Checker.getMemories().size()) { |
628 | 19 | spdlog::error(ErrCode::Value::InvalidMemoryIdx); |
629 | 19 | spdlog::error(ErrInfo::InfoForbidIndex( |
630 | 19 | ErrInfo::IndexCategory::Memory, Id, |
631 | 19 | static_cast<uint32_t>(Checker.getMemories().size()))); |
632 | 19 | return Unexpect(ErrCode::Value::InvalidMemoryIdx); |
633 | 19 | } |
634 | 104 | return {}; |
635 | 44 | case ExternalType::Tag: |
636 | 44 | if (Id >= Checker.getTags().size()) { |
637 | 33 | spdlog::error(ErrCode::Value::InvalidTagIdx); |
638 | 33 | spdlog::error(ErrInfo::InfoForbidIndex( |
639 | 33 | ErrInfo::IndexCategory::Tag, Id, |
640 | 33 | static_cast<uint32_t>(Checker.getTags().size()))); |
641 | 33 | return Unexpect(ErrCode::Value::InvalidTagIdx); |
642 | 33 | } |
643 | 11 | return {}; |
644 | 147 | case ExternalType::Global: |
645 | 147 | if (Id >= Checker.getGlobals().size()) { |
646 | 25 | spdlog::error(ErrCode::Value::InvalidGlobalIdx); |
647 | 25 | spdlog::error(ErrInfo::InfoForbidIndex( |
648 | 25 | ErrInfo::IndexCategory::Global, Id, |
649 | 25 | static_cast<uint32_t>(Checker.getGlobals().size()))); |
650 | 25 | return Unexpect(ErrCode::Value::InvalidGlobalIdx); |
651 | 25 | } |
652 | 122 | return {}; |
653 | 0 | default: |
654 | 0 | return {}; |
655 | 10.3k | } |
656 | 10.3k | } |
657 | | |
658 | 6.05k | Expect<void> Validator::validate(const AST::TypeSection &TypeSec) { |
659 | 6.05k | const auto STypeList = TypeSec.getContent(); |
660 | 6.05k | std::vector<uint32_t> SubTypeDepthMap(STypeList.size(), Unvisited); |
661 | 6.05k | uint32_t Idx = 0; |
662 | 13.4k | while (Idx < STypeList.size()) { |
663 | 7.43k | const auto &SType = STypeList[Idx]; |
664 | | // The next type to add takes this index in the type index space. |
665 | 7.43k | const uint32_t BaseIdx = static_cast<uint32_t>(Checker.getTypes().size()); |
666 | 7.43k | if (Conf.hasProposal(Proposal::GC)) { |
667 | | // With GC a type is (self-)recursive (a singleton is a rec group of 1): |
668 | | // add the whole group before validating so members can reference it. |
669 | 7.43k | const uint32_t RecSize = SType.getRecursiveInfo().has_value() |
670 | 7.43k | ? SType.getRecursiveInfo()->RecTypeSize |
671 | 7.43k | : 1; |
672 | 14.9k | for (uint32_t I = Idx; I < Idx + RecSize; I++) { |
673 | 7.47k | Checker.addType(STypeList[I]); |
674 | 7.47k | } |
675 | 14.8k | for (uint32_t I = Idx; I < Idx + RecSize; I++) { |
676 | 7.45k | EXPECTED_TRY( |
677 | 7.45k | validate(STypeList[I], BaseIdx + (I - Idx), SubTypeDepthMap) |
678 | 7.45k | .map_error([](auto E) { |
679 | 7.45k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Rec)); |
680 | 7.45k | return E; |
681 | 7.45k | })); |
682 | 7.45k | } |
683 | 7.38k | Idx += RecSize; |
684 | 7.38k | } else { |
685 | | // Without GC there are no rec groups: a type may reference only earlier |
686 | | // ones, so validate it before registering. |
687 | 0 | EXPECTED_TRY(validate(SType, BaseIdx, SubTypeDepthMap)); |
688 | 0 | Checker.addType(SType); |
689 | 0 | Idx++; |
690 | 0 | } |
691 | 7.43k | } |
692 | 5.99k | return {}; |
693 | 6.05k | } |
694 | | |
695 | | // Validate Import section. See "include/validator/validator.h". |
696 | 5.99k | Expect<void> Validator::validate(const AST::ImportSection &ImportSec) { |
697 | 5.99k | for (auto &ImportDesc : ImportSec.getContent()) { |
698 | 565 | EXPECTED_TRY(validate(ImportDesc).map_error([](auto E) { |
699 | 565 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Desc_Import)); |
700 | 565 | return E; |
701 | 565 | })); |
702 | 565 | } |
703 | 5.97k | return {}; |
704 | 5.99k | } |
705 | | |
706 | | // Validate Function section. See "include/validator/validator.h". |
707 | 5.97k | Expect<void> Validator::validate(const AST::FunctionSection &FuncSec) { |
708 | 5.97k | const auto &FuncVec = FuncSec.getContent(); |
709 | 5.97k | const auto &TypeVec = Checker.getTypes(); |
710 | | |
711 | | // Check whether the function type ID is valid in context. |
712 | 17.1k | for (auto &TId : FuncVec) { |
713 | 17.1k | if (TId >= TypeVec.size()) { |
714 | 10 | spdlog::error(ErrCode::Value::InvalidFuncTypeIdx); |
715 | 10 | spdlog::error( |
716 | 10 | ErrInfo::InfoForbidIndex(ErrInfo::IndexCategory::FunctionType, TId, |
717 | 10 | static_cast<uint32_t>(TypeVec.size()))); |
718 | 10 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Function)); |
719 | 10 | return Unexpect(ErrCode::Value::InvalidFuncTypeIdx); |
720 | 10 | } |
721 | 17.1k | if (!TypeVec[TId]->getCompositeType().isFunc()) { |
722 | 1 | spdlog::error(ErrCode::Value::InvalidFuncTypeIdx); |
723 | 1 | spdlog::error(" Defined type index {} is not a function type."sv, TId); |
724 | 1 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Function)); |
725 | 1 | return Unexpect(ErrCode::Value::InvalidFuncTypeIdx); |
726 | 1 | } |
727 | 17.1k | Checker.addFunc(TId); |
728 | 17.1k | } |
729 | 5.95k | return {}; |
730 | 5.97k | } |
731 | | |
732 | | // Validate Table section. See "include/validator/validator.h". |
733 | 5.95k | Expect<void> Validator::validate(const AST::TableSection &TabSec) { |
734 | 5.95k | for (auto &Tab : TabSec.getContent()) { |
735 | 821 | EXPECTED_TRY(validate(Tab).map_error([](auto E) { |
736 | 581 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Table)); |
737 | 581 | return E; |
738 | 581 | })); |
739 | 581 | Checker.addTable(Tab.getTableType()); |
740 | 581 | } |
741 | 5.71k | return {}; |
742 | 5.95k | } |
743 | | |
744 | | // Validate Memory section. See "include/validator/validator.h". |
745 | 5.71k | Expect<void> Validator::validate(const AST::MemorySection &MemSec) { |
746 | 5.71k | for (auto &Mem : MemSec.getContent()) { |
747 | 1.86k | EXPECTED_TRY(validate(Mem).map_error([](auto E) { |
748 | 1.68k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Type_Memory)); |
749 | 1.68k | return E; |
750 | 1.68k | })); |
751 | 1.68k | Checker.addMemory(Mem); |
752 | 1.68k | } |
753 | 5.53k | return {}; |
754 | 5.71k | } |
755 | | |
756 | | // Validate Global section. See "include/validator/validator.h". |
757 | 5.53k | Expect<void> Validator::validate(const AST::GlobalSection &GlobSec) { |
758 | 5.53k | for (auto &GlobSeg : GlobSec.getContent()) { |
759 | 608 | EXPECTED_TRY(validate(GlobSeg).map_error([](auto E) { |
760 | 297 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Global)); |
761 | 297 | return E; |
762 | 297 | })); |
763 | 297 | Checker.addGlobal(GlobSeg.getGlobalType()); |
764 | 297 | } |
765 | 5.22k | return {}; |
766 | 5.53k | } |
767 | | |
768 | | // Validate Element section. See "include/validator/validator.h". |
769 | 5.00k | Expect<void> Validator::validate(const AST::ElementSection &ElemSec) { |
770 | 5.00k | for (auto &ElemSeg : ElemSec.getContent()) { |
771 | 631 | EXPECTED_TRY(validate(ElemSeg).map_error([](auto E) { |
772 | 504 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Element)); |
773 | 504 | return E; |
774 | 504 | })); |
775 | 504 | Checker.addElem(ElemSeg); |
776 | 504 | } |
777 | 4.87k | return {}; |
778 | 5.00k | } |
779 | | |
780 | | // Validate Code section. See "include/validator/validator.h". |
781 | 4.85k | Expect<void> Validator::validate(const AST::CodeSection &CodeSec) { |
782 | 4.85k | const auto &CodeVec = CodeSec.getContent(); |
783 | 4.85k | const auto &FuncVec = Checker.getFunctions(); |
784 | | |
785 | | // Validate function body. |
786 | 16.2k | for (uint32_t Id = 0; Id < static_cast<uint32_t>(CodeVec.size()); ++Id) { |
787 | | // Added functions contain imported functions. |
788 | 12.8k | uint32_t TId = Id + static_cast<uint32_t>(Checker.getNumImportFuncs()); |
789 | 12.8k | if (TId >= static_cast<uint32_t>(FuncVec.size())) { |
790 | 0 | spdlog::error(ErrCode::Value::InvalidFuncIdx); |
791 | 0 | spdlog::error( |
792 | 0 | ErrInfo::InfoForbidIndex(ErrInfo::IndexCategory::Function, TId, |
793 | 0 | static_cast<uint32_t>(FuncVec.size()))); |
794 | 0 | return Unexpect(ErrCode::Value::InvalidFuncIdx); |
795 | 0 | } |
796 | 12.8k | EXPECTED_TRY(validate(CodeVec[Id], FuncVec[TId]).map_error([](auto E) { |
797 | 12.8k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Code)); |
798 | 12.8k | return E; |
799 | 12.8k | })); |
800 | 12.8k | } |
801 | 3.35k | return {}; |
802 | 4.85k | } |
803 | | |
804 | | // Validate Data section. See "include/validator/validator.h". |
805 | 4.87k | Expect<void> Validator::validate(const AST::DataSection &DataSec) { |
806 | 4.87k | for (auto &DataSeg : DataSec.getContent()) { |
807 | 362 | EXPECTED_TRY(validate(DataSeg).map_error([](auto E) { |
808 | 338 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Seg_Data)); |
809 | 338 | return E; |
810 | 338 | })); |
811 | 338 | Checker.addData(DataSeg); |
812 | 338 | } |
813 | 4.85k | return {}; |
814 | 4.87k | } |
815 | | |
816 | | // Validate Start section. See "include/validator/validator.h". |
817 | 5.05k | Expect<void> Validator::validate(const AST::StartSection &StartSec) { |
818 | 5.05k | if (StartSec.getContent()) { |
819 | 53 | auto FId = *StartSec.getContent(); |
820 | 53 | if (FId >= Checker.getFunctions().size()) { |
821 | 47 | spdlog::error(ErrCode::Value::InvalidFuncIdx); |
822 | 47 | spdlog::error(ErrInfo::InfoForbidIndex( |
823 | 47 | ErrInfo::IndexCategory::Function, FId, |
824 | 47 | static_cast<uint32_t>(Checker.getFunctions().size()))); |
825 | 47 | return Unexpect(ErrCode::Value::InvalidFuncIdx); |
826 | 47 | } |
827 | 6 | auto TId = Checker.getFunctions()[FId]; |
828 | 6 | assuming(TId < Checker.getTypes().size()); |
829 | 6 | if (!Checker.getTypes()[TId]->getCompositeType().isFunc()) { |
830 | 0 | spdlog::error(ErrCode::Value::InvalidStartFunc); |
831 | 0 | spdlog::error(" Defined type index {} is not a function type."sv, TId); |
832 | 0 | return Unexpect(ErrCode::Value::InvalidStartFunc); |
833 | 0 | } |
834 | 6 | auto &Type = Checker.getTypes()[TId]->getCompositeType().getFuncType(); |
835 | 6 | if (Type.getParamTypes().size() != 0 || Type.getReturnTypes().size() != 0) { |
836 | | // Start function signature should be {}->{} |
837 | 2 | spdlog::error(ErrCode::Value::InvalidStartFunc); |
838 | 2 | spdlog::error(ErrInfo::InfoMismatch({}, {}, Type.getParamTypes(), |
839 | 2 | Type.getReturnTypes())); |
840 | 2 | return Unexpect(ErrCode::Value::InvalidStartFunc); |
841 | 2 | } |
842 | 6 | } |
843 | 5.00k | return {}; |
844 | 5.05k | } |
845 | | |
846 | | // Validate Export section. See "include/validator/validator.h". |
847 | 5.17k | Expect<void> Validator::validate(const AST::ExportSection &ExportSec) { |
848 | 5.17k | std::unordered_set<std::string_view, Hash::Hash> ExportNames; |
849 | 10.3k | for (auto &ExportDesc : ExportSec.getContent()) { |
850 | 10.3k | auto Result = ExportNames.emplace(ExportDesc.getExternalName()); |
851 | 10.3k | if (!Result.second) { |
852 | | // Duplicated export name. |
853 | 5 | spdlog::error(ErrCode::Value::DupExportName); |
854 | 5 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Desc_Export)); |
855 | 5 | return Unexpect(ErrCode::Value::DupExportName); |
856 | 5 | } |
857 | 10.3k | EXPECTED_TRY(validate(ExportDesc).map_error([](auto E) { |
858 | 10.3k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Desc_Export)); |
859 | 10.3k | return E; |
860 | 10.3k | })); |
861 | 10.3k | } |
862 | 5.05k | return {}; |
863 | 5.17k | } |
864 | | |
865 | | // Validate Tag section. See "include/validator/validator.h". |
866 | 5.22k | Expect<void> Validator::validate(const AST::TagSection &TagSec) { |
867 | 5.22k | const auto &TagVec = TagSec.getContent(); |
868 | 5.22k | const auto &TypeVec = Checker.getTypes(); |
869 | | |
870 | | // Check whether the tag type ID is valid in context. |
871 | 5.22k | for (auto &TagType : TagVec) { |
872 | 119 | auto TagTypeIdx = TagType.getTypeIdx(); |
873 | 119 | if (TagTypeIdx >= TypeVec.size()) { |
874 | 38 | spdlog::error(ErrCode::Value::InvalidTagIdx); |
875 | 38 | spdlog::error( |
876 | 38 | ErrInfo::InfoForbidIndex(ErrInfo::IndexCategory::TagType, TagTypeIdx, |
877 | 38 | static_cast<uint32_t>(TypeVec.size()))); |
878 | 38 | return Unexpect(ErrCode::Value::InvalidTagIdx); |
879 | 38 | } |
880 | 81 | auto &CompType = TypeVec[TagTypeIdx]->getCompositeType(); |
881 | 81 | if (!CompType.isFunc()) { |
882 | 2 | spdlog::error(ErrCode::Value::InvalidTagIdx); |
883 | 2 | spdlog::error(" Defined type index {} is not a function type."sv, |
884 | 2 | TagTypeIdx); |
885 | 2 | return Unexpect(ErrCode::Value::InvalidTagIdx); |
886 | 2 | } |
887 | 79 | if (!CompType.getFuncType().getReturnTypes().empty()) { |
888 | 1 | spdlog::error(ErrCode::Value::InvalidTagResultType); |
889 | 1 | return Unexpect(ErrCode::Value::InvalidTagResultType); |
890 | 1 | } |
891 | 78 | Checker.addTag(TagTypeIdx); |
892 | 78 | } |
893 | 5.17k | return {}; |
894 | 5.22k | } |
895 | | |
896 | | // Validate constant expression. See "include/validator/validator.h". |
897 | | Expect<void> Validator::validateConstExpr(AST::InstrView Instrs, |
898 | 2.47k | Span<const ValType> Returns) { |
899 | 7.73k | for (auto &Instr : Instrs) { |
900 | | // Only these instructions are accepted. |
901 | 7.73k | switch (Instr.getOpCode()) { |
902 | 64 | case OpCode::Global__get: { |
903 | | // For the initialization case, global indices must be imported globals. |
904 | 64 | auto GlobIdx = Instr.getTargetIndex(); |
905 | 64 | uint32_t ValidGlobalSize = Checker.getNumImportGlobals(); |
906 | 64 | if (Conf.hasProposal(Proposal::FunctionReferences)) { |
907 | 64 | ValidGlobalSize = static_cast<uint32_t>(Checker.getGlobals().size()); |
908 | 64 | } |
909 | 64 | if (GlobIdx >= ValidGlobalSize) { |
910 | 47 | spdlog::error(ErrCode::Value::InvalidGlobalIdx); |
911 | 47 | spdlog::error(ErrInfo::InfoForbidIndex(ErrInfo::IndexCategory::Global, |
912 | 47 | GlobIdx, ValidGlobalSize)); |
913 | 47 | spdlog::error( |
914 | 47 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
915 | 47 | return Unexpect(ErrCode::Value::InvalidGlobalIdx); |
916 | 47 | } |
917 | 17 | if (Checker.getGlobals()[GlobIdx].second != ValMut::Const) { |
918 | 1 | spdlog::error(ErrCode::Value::ConstExprRequired); |
919 | 1 | spdlog::error( |
920 | 1 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
921 | 1 | return Unexpect(ErrCode::Value::ConstExprRequired); |
922 | 1 | } |
923 | 16 | break; |
924 | 17 | } |
925 | 1.32k | case OpCode::Ref__func: { |
926 | | // In a const expression, add the reference to the context. |
927 | 1.32k | auto FuncIdx = Instr.getTargetIndex(); |
928 | 1.32k | if (FuncIdx >= Checker.getFunctions().size()) { |
929 | | // Function index out of range. |
930 | 44 | spdlog::error(ErrCode::Value::InvalidFuncIdx); |
931 | 44 | spdlog::error(ErrInfo::InfoForbidIndex( |
932 | 44 | ErrInfo::IndexCategory::Function, FuncIdx, |
933 | 44 | static_cast<uint32_t>(Checker.getFunctions().size()))); |
934 | 44 | spdlog::error( |
935 | 44 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
936 | 44 | return Unexpect(ErrCode::Value::InvalidFuncIdx); |
937 | 44 | } |
938 | 1.27k | Checker.addRef(Instr.getTargetIndex()); |
939 | 1.27k | break; |
940 | 1.32k | } |
941 | 1.34k | case OpCode::I32__const: |
942 | 1.74k | case OpCode::I64__const: |
943 | 1.83k | case OpCode::F32__const: |
944 | 1.92k | case OpCode::F64__const: |
945 | 2.10k | case OpCode::Ref__null: |
946 | 2.13k | case OpCode::V128__const: |
947 | 4.41k | case OpCode::End: |
948 | 4.45k | case OpCode::Struct__new: |
949 | 4.47k | case OpCode::Struct__new_default: |
950 | 4.49k | case OpCode::Array__new: |
951 | 4.53k | case OpCode::Array__new_default: |
952 | 4.56k | case OpCode::Array__new_fixed: |
953 | 4.61k | case OpCode::Any__convert_extern: |
954 | 4.67k | case OpCode::Extern__convert_any: |
955 | 4.76k | case OpCode::Ref__i31: |
956 | 4.76k | break; |
957 | | |
958 | | // For the Extended-const proposal, these instructions are accepted. |
959 | 196 | case OpCode::I32__add: |
960 | 447 | case OpCode::I32__sub: |
961 | 788 | case OpCode::I32__mul: |
962 | 1.03k | case OpCode::I64__add: |
963 | 1.27k | case OpCode::I64__sub: |
964 | 1.48k | case OpCode::I64__mul: |
965 | 1.48k | if (Conf.hasProposal(Proposal::ExtendedConst)) { |
966 | 1.48k | break; |
967 | 1.48k | } |
968 | 0 | spdlog::error(ErrCode::Value::ConstExprRequired); |
969 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::ExtendedConst)); |
970 | 0 | spdlog::error( |
971 | 0 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
972 | 0 | return Unexpect(ErrCode::Value::ConstExprRequired); |
973 | | |
974 | 99 | default: |
975 | 99 | spdlog::error(ErrCode::Value::ConstExprRequired); |
976 | 99 | spdlog::error( |
977 | 99 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
978 | 99 | return Unexpect(ErrCode::Value::ConstExprRequired); |
979 | 7.73k | } |
980 | 7.73k | } |
981 | | // Validate expression with result types. |
982 | 2.28k | Checker.reset(); |
983 | 2.28k | return Checker.validate(Instrs, Returns); |
984 | 2.47k | } |
985 | | |
986 | | } // namespace Validator |
987 | | } // namespace WasmEdge |