/src/jsonnet/core/static_analysis.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Copyright 2015 Google Inc. All rights reserved. |
3 | | |
4 | | Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | you may not use this file except in compliance with the License. |
6 | | You may obtain a copy of the License at |
7 | | |
8 | | http://www.apache.org/licenses/LICENSE-2.0 |
9 | | |
10 | | Unless required by applicable law or agreed to in writing, software |
11 | | distributed under the License is distributed on an "AS IS" BASIS, |
12 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | See the License for the specific language governing permissions and |
14 | | limitations under the License. |
15 | | */ |
16 | | |
17 | | #include <set> |
18 | | |
19 | | #include "ast.h" |
20 | | #include "static_analysis.h" |
21 | | #include "static_error.h" |
22 | | |
23 | | namespace jsonnet::internal { |
24 | | |
25 | | typedef std::set<const Identifier *> IdSet; |
26 | | |
27 | | /** Inserts all of s into r. */ |
28 | | static void append(IdSet &r, const IdSet &s) |
29 | 616M | { |
30 | 616M | r.insert(s.begin(), s.end()); |
31 | 616M | } |
32 | | |
33 | | /** Statically analyse the given ast. |
34 | | * |
35 | | * \param ast_ The AST. |
36 | | * \param in_object Whether or not ast_ is within the lexical scope of an object AST. |
37 | | * \param vars The variables defined within lexical scope of ast_. |
38 | | * \returns The free variables in ast_. |
39 | | */ |
40 | | static IdSet static_analysis(AST *ast_, bool in_object, const IdSet &vars) |
41 | 563M | { |
42 | 563M | IdSet r; |
43 | | |
44 | 563M | switch (ast_->type) { |
45 | 50.5M | case AST_APPLY: { |
46 | 50.5M | assert(dynamic_cast<Apply *>(ast_)); |
47 | 0 | auto* ast = static_cast<Apply *>(ast_); |
48 | 50.5M | append(r, static_analysis(ast->target, in_object, vars)); |
49 | 50.5M | for (const auto &arg : ast->args) |
50 | 83.3M | append(r, static_analysis(arg.expr, in_object, vars)); |
51 | 50.5M | } break; |
52 | 0 | case AST_APPLY_BRACE: { |
53 | 0 | assert(dynamic_cast<ApplyBrace *>(ast_)); |
54 | | // Nothing to do. |
55 | 0 | } break; |
56 | 11.5M | case AST_ARRAY: { |
57 | 11.5M | assert(dynamic_cast<Array *>(ast_)); |
58 | 0 | auto* ast = static_cast<Array *>(ast_); |
59 | 11.5M | for (auto &el : ast->elements) |
60 | 19.1M | append(r, static_analysis(el.expr, in_object, vars)); |
61 | 11.5M | } break; |
62 | 51.0M | case AST_BINARY: { |
63 | 51.0M | assert(dynamic_cast<Binary *>(ast_)); |
64 | 0 | auto* ast = static_cast<Binary *>(ast_); |
65 | 51.0M | append(r, static_analysis(ast->left, in_object, vars)); |
66 | 51.0M | append(r, static_analysis(ast->right, in_object, vars)); |
67 | 51.0M | } break; |
68 | 717k | case AST_BUILTIN_FUNCTION: { |
69 | 717k | assert(dynamic_cast<BuiltinFunction *>(ast_)); |
70 | | // Nothing to do. |
71 | 717k | } break; |
72 | 20.7M | case AST_CONDITIONAL: { |
73 | 20.7M | assert(dynamic_cast<Conditional *>(ast_)); |
74 | 0 | auto* ast = static_cast<Conditional *>(ast_); |
75 | 20.7M | append(r, static_analysis(ast->cond, in_object, vars)); |
76 | 20.7M | append(r, static_analysis(ast->branchTrue, in_object, vars)); |
77 | 20.7M | append(r, static_analysis(ast->branchFalse, in_object, vars)); |
78 | 20.7M | } break; |
79 | 10.9M | case AST_ERROR: { |
80 | 10.9M | assert(dynamic_cast<Error *>(ast_)); |
81 | 0 | auto* ast = static_cast<Error *>(ast_); |
82 | 10.9M | append(r, static_analysis(ast->expr, in_object, vars)); |
83 | 10.9M | } break; |
84 | 12.7M | case AST_FUNCTION: { |
85 | 12.7M | assert(dynamic_cast<Function *>(ast_)); |
86 | 0 | auto* ast = static_cast<Function *>(ast_); |
87 | 12.7M | auto new_vars = vars; |
88 | 12.7M | IdSet params; |
89 | 23.3M | for (const auto &p : ast->params) { |
90 | 23.3M | if (params.find(p.id) != params.end()) { |
91 | 43 | std::string msg = "Duplicate function parameter: " + encode_utf8(p.id->name); |
92 | 43 | throw StaticError(ast_->location, msg); |
93 | 43 | } |
94 | 23.3M | params.insert(p.id); |
95 | 23.3M | new_vars.insert(p.id); |
96 | 23.3M | } |
97 | | |
98 | 12.7M | auto fv = static_analysis(ast->body, in_object, new_vars); |
99 | 23.3M | for (const auto &p : ast->params) { |
100 | 23.3M | if (p.expr != nullptr) |
101 | 392k | append(fv, static_analysis(p.expr, in_object, new_vars)); |
102 | 23.3M | } |
103 | 12.7M | for (const auto &p : ast->params) |
104 | 23.3M | fv.erase(p.id); |
105 | 12.7M | append(r, fv); |
106 | 12.7M | } break; |
107 | 850 | case AST_IMPORT: { |
108 | 850 | assert(dynamic_cast<Import *>(ast_)); |
109 | | // Nothing to do. |
110 | 850 | } break; |
111 | 650 | case AST_IMPORTSTR: { |
112 | 650 | assert(dynamic_cast<Importstr *>(ast_)); |
113 | | // Nothing to do. |
114 | 650 | } break; |
115 | 559 | case AST_IMPORTBIN: { |
116 | 559 | assert(dynamic_cast<Importbin *>(ast_)); |
117 | | // Nothing to do. |
118 | 559 | } break; |
119 | 204k | case AST_IN_SUPER: { |
120 | 204k | assert(dynamic_cast<const InSuper *>(ast_)); |
121 | 0 | auto* ast = static_cast<const InSuper *>(ast_); |
122 | 204k | if (!in_object) |
123 | 15 | throw StaticError(ast_->location, "Can't use super outside of an object."); |
124 | 204k | append(r, static_analysis(ast->element, in_object, vars)); |
125 | 204k | } break; |
126 | 55.1M | case AST_INDEX: { |
127 | 55.1M | assert(dynamic_cast<const Index *>(ast_)); |
128 | 0 | auto* ast = static_cast<const Index *>(ast_); |
129 | 55.1M | append(r, static_analysis(ast->target, in_object, vars)); |
130 | 55.1M | append(r, static_analysis(ast->index, in_object, vars)); |
131 | 55.1M | } break; |
132 | 26.7M | case AST_LOCAL: { |
133 | 26.7M | assert(dynamic_cast<const Local *>(ast_)); |
134 | 0 | auto* ast = static_cast<const Local *>(ast_); |
135 | 26.7M | IdSet ast_vars; |
136 | 44.7M | for (const auto &bind : ast->binds) { |
137 | 44.7M | ast_vars.insert(bind.var); |
138 | 44.7M | } |
139 | 26.7M | auto new_vars = vars; |
140 | 26.7M | append(new_vars, ast_vars); |
141 | 26.7M | IdSet fvs; |
142 | 44.6M | for (const auto &bind : ast->binds) { |
143 | 44.6M | append(fvs, static_analysis(bind.body, in_object, new_vars)); |
144 | 44.6M | } |
145 | | |
146 | 26.7M | append(fvs, static_analysis(ast->body, in_object, new_vars)); |
147 | | |
148 | 26.7M | for (const auto &bind : ast->binds) |
149 | 44.5M | fvs.erase(bind.var); |
150 | | |
151 | 26.7M | append(r, fvs); |
152 | 26.7M | } break; |
153 | 1.74M | case AST_LITERAL_BOOLEAN: { |
154 | 1.74M | assert(dynamic_cast<const LiteralBoolean *>(ast_)); |
155 | | // Nothing to do. |
156 | 1.74M | } break; |
157 | 39.1M | case AST_LITERAL_NUMBER: { |
158 | 39.1M | assert(dynamic_cast<const LiteralNumber *>(ast_)); |
159 | | // Nothing to do. |
160 | 39.1M | } break; |
161 | 98.3M | case AST_LITERAL_STRING: { |
162 | 98.3M | assert(dynamic_cast<const LiteralString *>(ast_)); |
163 | | // Nothing to do. |
164 | 98.3M | } break; |
165 | 923k | case AST_LITERAL_NULL: { |
166 | 923k | assert(dynamic_cast<const LiteralNull *>(ast_)); |
167 | | // Nothing to do. |
168 | 923k | } break; |
169 | 3.58M | case AST_DESUGARED_OBJECT: { |
170 | 3.58M | assert(dynamic_cast<DesugaredObject *>(ast_)); |
171 | 0 | auto* ast = static_cast<DesugaredObject *>(ast_); |
172 | 15.4M | for (auto &field : ast->fields) { |
173 | 15.4M | append(r, static_analysis(field.name, in_object, vars)); |
174 | 15.4M | append(r, static_analysis(field.body, true, vars)); |
175 | 15.4M | } |
176 | 3.58M | for (AST *assert : ast->asserts) { |
177 | 255k | append(r, static_analysis(assert, true, vars)); |
178 | 255k | } |
179 | 3.58M | } break; |
180 | 2.15M | case AST_OBJECT_COMPREHENSION_SIMPLE: { |
181 | 2.15M | assert(dynamic_cast<ObjectComprehensionSimple *>(ast_)); |
182 | 0 | auto* ast = static_cast<ObjectComprehensionSimple *>(ast_); |
183 | 2.15M | auto new_vars = vars; |
184 | 2.15M | new_vars.insert(ast->id); |
185 | 2.15M | append(r, static_analysis(ast->field, false, new_vars)); |
186 | 2.15M | append(r, static_analysis(ast->value, true, new_vars)); |
187 | 2.15M | r.erase(ast->id); |
188 | 2.15M | append(r, static_analysis(ast->array, in_object, vars)); |
189 | 2.15M | } break; |
190 | 4.50M | case AST_SELF: { |
191 | 4.50M | assert(dynamic_cast<const Self *>(ast_)); |
192 | 4.50M | if (!in_object) |
193 | 6 | throw StaticError(ast_->location, "Can't use self outside of an object."); |
194 | 4.50M | } break; |
195 | 4.50M | case AST_SUPER_INDEX: { |
196 | 201k | assert(dynamic_cast<const SuperIndex *>(ast_)); |
197 | 0 | auto* ast = static_cast<const SuperIndex *>(ast_); |
198 | 201k | if (!in_object) |
199 | 26 | throw StaticError(ast_->location, "Can't use super outside of an object."); |
200 | 201k | append(r, static_analysis(ast->index, in_object, vars)); |
201 | 201k | } break; |
202 | 1.72M | case AST_UNARY: { |
203 | 1.72M | assert(dynamic_cast<const Unary *>(ast_)); |
204 | 0 | auto* ast = static_cast<const Unary *>(ast_); |
205 | 1.72M | append(r, static_analysis(ast->expr, in_object, vars)); |
206 | 1.72M | } break; |
207 | 170M | case AST_VAR: { |
208 | 170M | assert(dynamic_cast<const Var *>(ast_)); |
209 | 0 | auto* ast = static_cast<const Var *>(ast_); |
210 | 170M | if (vars.find(ast->id) == vars.end()) { |
211 | 1.84k | throw StaticError(ast->location, "Unknown variable: " + encode_utf8(ast->id->name)); |
212 | 1.84k | } |
213 | 170M | r.insert(ast->id); |
214 | 170M | } break; |
215 | 0 | default: |
216 | 0 | std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_ << std::endl; |
217 | 0 | std::abort(); |
218 | 0 | break; |
219 | 563M | } |
220 | | |
221 | 562M | for (auto *id : r) |
222 | 737M | ast_->freeVariables.push_back(id); |
223 | | |
224 | 562M | return r; |
225 | 563M | } |
226 | | |
227 | | void jsonnet_static_analysis(AST *ast) |
228 | 18.3k | { |
229 | 18.3k | static_analysis(ast, false, IdSet{}); |
230 | 18.3k | } |
231 | | |
232 | | } // namespace jsonnet::internal |