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