Coverage Report

Created: 2026-09-28 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/include/validator/component_context.h
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
//===-- wasmedge/validator/component_context.h - Component state ----------===//
5
//
6
// Part of the WasmEdge Project.
7
//
8
//===----------------------------------------------------------------------===//
9
///
10
/// \file
11
/// This file contains the state of one component under validation: the scope
12
/// stack, its index spaces, and everything that reads them. An operation that
13
/// reads an index space lives here. Every other type operation lives in
14
/// TypeSystem (component_types.h).
15
///
16
//===----------------------------------------------------------------------===//
17
#pragma once
18
19
#include "ast/component/canonical.h"
20
#include "ast/component/instance.h"
21
#include "ast/component/sort.h"
22
#include "ast/module.h"
23
#include "common/errcode.h"
24
#include "common/span.h"
25
#include "validator/component_name.h"
26
#include "validator/component_types.h"
27
#include "validator/formchecker.h"
28
29
#include <deque>
30
#include <optional>
31
#include <string>
32
#include <string_view>
33
#include <utility>
34
#include <vector>
35
36
namespace WasmEdge {
37
namespace Validator {
38
namespace Component {
39
40
class Context {
41
public:
42
13.6k
  explicit Context(TypeSystem &Types) noexcept : Types(Types) {}
43
44
  // ==========================================================================
45
  // The scope stack. Scopes stay owned after they pop; validate() resets it.
46
  // ==========================================================================
47
48
1.24M
  Scope &enterScope(ScopeKind K) noexcept {
49
1.24M
    const Scope *Parent = Stack.empty() ? nullptr : Stack.back();
50
1.24M
    OwnedScopes.emplace_back(K, Parent);
51
1.24M
    Stack.push_back(&OwnedScopes.back());
52
1.24M
    return OwnedScopes.back();
53
1.24M
  }
54
55
1.24M
  void exitScope() noexcept {
56
1.24M
    assuming(!Stack.empty());
57
1.24M
    Stack.pop_back();
58
1.24M
  }
59
60
9.74M
  Scope &top() noexcept {
61
9.74M
    assuming(!Stack.empty());
62
9.74M
    return *Stack.back();
63
9.74M
  }
64
16.9k
  const Scope &top() const noexcept {
65
16.9k
    assuming(!Stack.empty());
66
16.9k
    return *Stack.back();
67
16.9k
  }
68
69
  /// The scope Levels hops up from the current one; nullptr if out of range.
70
5.50k
  Scope *scopeUp(uint32_t Levels) noexcept {
71
5.50k
    if (Levels >= Stack.size()) {
72
33
      return nullptr;
73
33
    }
74
5.46k
    return Stack[Stack.size() - 1 - Levels];
75
5.50k
  }
76
77
  /// The scope inside the outer-alias target; a component there is a crossing.
78
2.44k
  const Scope *scopeInsideTarget(uint32_t Levels) const noexcept {
79
2.44k
    if (Levels == 0 || Levels > Stack.size()) {
80
1.93k
      return nullptr;
81
1.93k
    }
82
508
    return Stack[Stack.size() - Levels];
83
2.44k
  }
84
85
  // ==========================================================================
86
  // Index-space registration and resolution.
87
  // ==========================================================================
88
89
  /// Push the entity described by a resolved view into its index space.
90
  void defineExtern(const ExternInfo &Info) noexcept;
91
  /// Resolve a sortidx to its typed view in the current scope.
92
  Expect<ExternInfo>
93
  resolveSortIndex(const AST::Component::SortIndex &SI) noexcept;
94
  /// Build the external view of an inline core module.
95
  Expect<const CoreShape *> buildCoreShape(const AST::Module &Mod) noexcept;
96
97
  // ==========================================================================
98
  // Naming checks and the named-types rule; the only writer of NameSide.
99
  // ==========================================================================
100
101
  /// Extern-name grammar checks; an export rejects the import-only name kinds.
102
  Expect<ExternName> parseExternName(std::string_view Name,
103
                                     bool IsImport) const noexcept;
104
  /// Build the comparison record for a parsed name.
105
  NameRecord makeNameRecord(const ExternName &Name) const noexcept;
106
  /// Append N to Names, or diagnose the clash (exact or strong-uniqueness).
107
  Expect<void> addUniqueName(std::vector<NameRecord> &Names,
108
                             const NameRecord &N, bool IsImport) const noexcept;
109
  /// Name attributes: each kind at most once, on a plain, instance-typed name.
110
  Expect<void> checkNameAttributes(const ExternName &CN,
111
                                   Span<const std::string> Impls,
112
                                   Span<const std::string> ExtIds,
113
                                   Span<const std::string> VSuffixes,
114
                                   bool IsInstance) const noexcept;
115
  /// Annotated plainname rules ([constructor]/[method]/[static]).
116
  Expect<void> checkAnnotatedName(const ExternName &Name,
117
                                  const ExternInfo &Info,
118
                                  bool IsImport) noexcept;
119
  /// Track plain resource labels for later annotated-name checks.
120
  void recordResourceLabel(const ExternName &Name, const ExternInfo &Info,
121
                           bool IsImport) noexcept;
122
  /// Named-types rule: an earlier extern must introduce each named type used.
123
  Expect<void> checkNamedTypesRule(const ExternInfo &Info,
124
                                   bool IsImport) noexcept;
125
126
  // ==========================================================================
127
  // Declaring an import or an export; name errors precede ascription errors.
128
  // ==========================================================================
129
130
  /// Check an import's name and define the resolved entity it introduces.
131
  Expect<ExternInfo>
132
  defineImport(std::string_view Name, const ExternInfo &Resolved,
133
               Span<const std::string> Impls = {},
134
               Span<const std::string> ExtIds = {},
135
               Span<const std::string> VSuffixes = {}) noexcept;
136
  /// Check and record an export's name; defineExport then defines the entity.
137
  Expect<ExternName>
138
  registerExportName(std::string_view Name, bool IsInstance,
139
                     Span<const std::string> Impls = {},
140
                     Span<const std::string> ExtIds = {},
141
                     Span<const std::string> VSuffixes = {}) noexcept;
142
  /// Apply the optional ascription and define the re-exported index.
143
  Expect<ExternInfo>
144
  defineExport(const ExternName &CN, const ExternInfo &Inferred,
145
               const std::optional<ExternInfo> &Ascribed) noexcept;
146
147
  /// Mint fresh identities for the resources Inst declares; top() owns them.
148
  const Shape *freshenDeclaredResources(const Shape *Inst,
149
153
                                        bool FromImport) noexcept {
150
153
    return Types.freshenDeclaredResources(Inst, top(), FromImport);
151
153
  }
152
153
  // ==========================================================================
154
  // Canonical options. They resolve core index spaces, so they live here.
155
  // ==========================================================================
156
157
  /// The index type of the selected canonical memory.
158
  ValType
159
  getCanonPtrType(const AST::Component::Canonical &Canon) const noexcept;
160
  /// canonopt rules: duplicates, indices, core signatures, per-site whitelist.
161
  Expect<void> checkOptions(const AST::Component::Canonical &Canon,
162
                            bool IsLift) const noexcept;
163
  /// The memory and realloc options a definition (What) needs.
164
  Expect<void> requireOptions(const AST::Component::Canonical &Canon,
165
                              bool NeedMemory, bool NeedRealloc,
166
                              std::string_view What) const noexcept;
167
168
  // ==========================================================================
169
  // Instantiation.
170
  // ==========================================================================
171
172
  /// Match args against CI.Imports and produce the freshened export view.
173
  Expect<const Shape *> instantiateComponentShape(
174
      const Shape &CI,
175
      Span<const AST::Component::InstantiateArg<AST::Component::SortIndex>>
176
          Args) noexcept;
177
178
  /// Core-module code sections deferred to the end of the root component.
179
  std::vector<std::pair<const AST::Module *, FormChecker>> DeferredModules;
180
181
5.98k
  void reset() noexcept {
182
5.98k
    DeferredModules.clear();
183
5.98k
    Stack.clear();
184
5.98k
    OwnedScopes.clear();
185
5.98k
    Types.reset();
186
5.98k
  }
187
188
private:
189
  // Named-types rule: introduceExternTypes checks (false if unmet) and records.
190
  bool introduceExternTypes(const ExternInfo &Info, bool IsImport) noexcept;
191
  bool isIntroduced(const QualValType &Q, bool IsImport) noexcept;
192
  bool isIntroduced(const TypeEntry &E, bool IsImport) noexcept;
193
  // The immediate components of the type. The extern names the type itself.
194
  bool areInnerTypesIntroduced(const TypeEntry &E, bool IsImport) noexcept;
195
196
  TypeSystem &Types;
197
  std::vector<Scope *> Stack;
198
  std::deque<Scope> OwnedScopes;
199
};
200
201
} // namespace Component
202
} // namespace Validator
203
} // namespace WasmEdge