1 // Copyright 2021 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "fuzzers/tint_ast_fuzzer/mutations/replace_identifier.h"
16
17 #include <utility>
18
19 #include "fuzzers/tint_ast_fuzzer/util.h"
20 #include "src/program_builder.h"
21
22 namespace tint {
23 namespace fuzzers {
24 namespace ast_fuzzer {
25
MutationReplaceIdentifier(protobufs::MutationReplaceIdentifier message)26 MutationReplaceIdentifier::MutationReplaceIdentifier(
27 protobufs::MutationReplaceIdentifier message)
28 : message_(std::move(message)) {}
29
MutationReplaceIdentifier(uint32_t use_id,uint32_t replacement_id)30 MutationReplaceIdentifier::MutationReplaceIdentifier(uint32_t use_id,
31 uint32_t replacement_id) {
32 message_.set_use_id(use_id);
33 message_.set_replacement_id(replacement_id);
34 }
35
IsApplicable(const tint::Program & program,const NodeIdMap & node_id_map) const36 bool MutationReplaceIdentifier::IsApplicable(
37 const tint::Program& program,
38 const NodeIdMap& node_id_map) const {
39 const auto* use_ast_node = tint::As<ast::IdentifierExpression>(
40 node_id_map.GetNode(message_.use_id()));
41 if (!use_ast_node) {
42 // Either the `use_id` is invalid or the node is not an
43 // `IdentifierExpression`.
44 return false;
45 }
46
47 const auto* use_sem_node =
48 tint::As<sem::VariableUser>(program.Sem().Get(use_ast_node));
49 if (!use_sem_node) {
50 // Either the semantic information is not present for a `use_node` or that
51 // node is not a variable user.
52 return false;
53 }
54
55 const auto* replacement_ast_node =
56 tint::As<ast::Variable>(node_id_map.GetNode(message_.replacement_id()));
57 if (!replacement_ast_node) {
58 // Either the `replacement_id` is invalid or is not an id of a variable.
59 return false;
60 }
61
62 const auto* replacement_sem_node = program.Sem().Get(replacement_ast_node);
63 if (!replacement_sem_node) {
64 return false;
65 }
66
67 if (replacement_sem_node == use_sem_node->Variable()) {
68 return false;
69 }
70
71 auto in_scope =
72 util::GetAllVarsInScope(program, use_sem_node->Stmt(),
73 [replacement_sem_node](const sem::Variable* var) {
74 return var == replacement_sem_node;
75 });
76 if (in_scope.empty()) {
77 // The replacement variable is not in scope.
78 return false;
79 }
80
81 return use_sem_node->Type() == replacement_sem_node->Type();
82 }
83
Apply(const NodeIdMap & node_id_map,tint::CloneContext * clone_context,NodeIdMap * new_node_id_map) const84 void MutationReplaceIdentifier::Apply(const NodeIdMap& node_id_map,
85 tint::CloneContext* clone_context,
86 NodeIdMap* new_node_id_map) const {
87 const auto* use_node = node_id_map.GetNode(message_.use_id());
88 const auto* replacement_var =
89 tint::As<ast::Variable>(node_id_map.GetNode(message_.replacement_id()));
90
91 auto* cloned_replacement =
92 clone_context->dst->Expr(clone_context->Clone(use_node->source),
93 clone_context->Clone(replacement_var->symbol));
94 clone_context->Replace(use_node, cloned_replacement);
95 new_node_id_map->Add(cloned_replacement, message_.use_id());
96 }
97
ToMessage() const98 protobufs::Mutation MutationReplaceIdentifier::ToMessage() const {
99 protobufs::Mutation mutation;
100 *mutation.mutable_replace_identifier() = message_;
101 return mutation;
102 }
103
104 } // namespace ast_fuzzer
105 } // namespace fuzzers
106 } // namespace tint
107