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