/src/jsonnet/core/static_analysis.cpp
Line | Count | Source |
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 | 737M | { |
30 | 737M | r.insert(s.begin(), s.end()); |
31 | 737M | } |
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 | 673M | { |
42 | 673M | IdSet r; |
43 | | |
44 | 673M | switch (ast_->type) { |
45 | 60.3M | case AST_APPLY: { |
46 | 60.3M | assert(dynamic_cast<Apply *>(ast_)); |
47 | 60.3M | auto* ast = static_cast<Apply *>(ast_); |
48 | 60.3M | append(r, static_analysis(ast->target, in_object, vars)); |
49 | 60.3M | for (const auto &arg : ast->args) |
50 | 99.1M | append(r, static_analysis(arg.expr, in_object, vars)); |
51 | 60.3M | } break; |
52 | 0 | case AST_APPLY_BRACE: { |
53 | 0 | assert(dynamic_cast<ApplyBrace *>(ast_)); |
54 | | // Nothing to do. |
55 | 0 | } break; |
56 | 14.1M | case AST_ARRAY: { |
57 | 14.1M | assert(dynamic_cast<Array *>(ast_)); |
58 | 14.1M | auto* ast = static_cast<Array *>(ast_); |
59 | 14.1M | for (auto &el : ast->elements) |
60 | 19.3M | append(r, static_analysis(el.expr, in_object, vars)); |
61 | 14.1M | } break; |
62 | 61.7M | case AST_BINARY: { |
63 | 61.7M | assert(dynamic_cast<Binary *>(ast_)); |
64 | 61.7M | auto* ast = static_cast<Binary *>(ast_); |
65 | 61.7M | append(r, static_analysis(ast->left, in_object, vars)); |
66 | 61.7M | append(r, static_analysis(ast->right, in_object, vars)); |
67 | 61.7M | } break; |
68 | 897k | case AST_BUILTIN_FUNCTION: { |
69 | 897k | assert(dynamic_cast<BuiltinFunction *>(ast_)); |
70 | | // Nothing to do. |
71 | 897k | } break; |
72 | 24.8M | case AST_CONDITIONAL: { |
73 | 24.8M | assert(dynamic_cast<Conditional *>(ast_)); |
74 | 24.8M | auto* ast = static_cast<Conditional *>(ast_); |
75 | 24.8M | append(r, static_analysis(ast->cond, in_object, vars)); |
76 | 24.8M | append(r, static_analysis(ast->branchTrue, in_object, vars)); |
77 | 24.8M | append(r, static_analysis(ast->branchFalse, in_object, vars)); |
78 | 24.8M | } break; |
79 | 13.0M | case AST_ERROR: { |
80 | 13.0M | assert(dynamic_cast<Error *>(ast_)); |
81 | 13.0M | auto* ast = static_cast<Error *>(ast_); |
82 | 13.0M | append(r, static_analysis(ast->expr, in_object, vars)); |
83 | 13.0M | } break; |
84 | 15.3M | case AST_FUNCTION: { |
85 | 15.3M | assert(dynamic_cast<Function *>(ast_)); |
86 | 15.3M | auto* ast = static_cast<Function *>(ast_); |
87 | 15.3M | auto new_vars = vars; |
88 | 15.3M | IdSet params; |
89 | 28.1M | for (const auto &p : ast->params) { |
90 | 28.1M | if (params.find(p.id) != params.end()) { |
91 | 14 | std::string msg = "Duplicate function parameter: " + encode_utf8(p.id->name); |
92 | 14 | throw StaticError(ast_->location, msg); |
93 | 14 | } |
94 | 28.1M | params.insert(p.id); |
95 | 28.1M | new_vars.insert(p.id); |
96 | 28.1M | } |
97 | | |
98 | 15.3M | auto fv = static_analysis(ast->body, in_object, new_vars); |
99 | 28.1M | for (const auto &p : ast->params) { |
100 | 28.1M | if (p.expr != nullptr) |
101 | 454k | append(fv, static_analysis(p.expr, in_object, new_vars)); |
102 | 28.1M | } |
103 | 15.3M | for (const auto &p : ast->params) |
104 | 28.1M | fv.erase(p.id); |
105 | 15.3M | append(r, fv); |
106 | 15.3M | } break; |
107 | 769 | case AST_IMPORT: { |
108 | 769 | assert(dynamic_cast<Import *>(ast_)); |
109 | | // Nothing to do. |
110 | 769 | } break; |
111 | 1.95k | case AST_IMPORTSTR: { |
112 | 1.95k | assert(dynamic_cast<Importstr *>(ast_)); |
113 | | // Nothing to do. |
114 | 1.95k | } break; |
115 | 21.3k | case AST_IMPORTBIN: { |
116 | 21.3k | assert(dynamic_cast<Importbin *>(ast_)); |
117 | | // Nothing to do. |
118 | 21.3k | } break; |
119 | 237k | case AST_IN_SUPER: { |
120 | 237k | assert(dynamic_cast<const InSuper *>(ast_)); |
121 | 237k | auto* ast = static_cast<const InSuper *>(ast_); |
122 | 237k | if (!in_object) |
123 | 66 | throw StaticError(ast_->location, "Can't use super outside of an object."); |
124 | 237k | append(r, static_analysis(ast->element, in_object, vars)); |
125 | 237k | } break; |
126 | 66.0M | case AST_INDEX: { |
127 | 66.0M | assert(dynamic_cast<const Index *>(ast_)); |
128 | 66.0M | auto* ast = static_cast<const Index *>(ast_); |
129 | 66.0M | append(r, static_analysis(ast->target, in_object, vars)); |
130 | 66.0M | append(r, static_analysis(ast->index, in_object, vars)); |
131 | 66.0M | } break; |
132 | 32.0M | case AST_LOCAL: { |
133 | 32.0M | assert(dynamic_cast<const Local *>(ast_)); |
134 | 32.0M | auto* ast = static_cast<const Local *>(ast_); |
135 | 32.0M | IdSet ast_vars; |
136 | 54.4M | for (const auto &bind : ast->binds) { |
137 | 54.4M | ast_vars.insert(bind.var); |
138 | 54.4M | } |
139 | 32.0M | auto new_vars = vars; |
140 | 32.0M | append(new_vars, ast_vars); |
141 | 32.0M | IdSet fvs; |
142 | 54.3M | for (const auto &bind : ast->binds) { |
143 | 54.3M | append(fvs, static_analysis(bind.body, in_object, new_vars)); |
144 | 54.3M | } |
145 | | |
146 | 32.0M | append(fvs, static_analysis(ast->body, in_object, new_vars)); |
147 | | |
148 | 32.0M | for (const auto &bind : ast->binds) |
149 | 54.2M | fvs.erase(bind.var); |
150 | | |
151 | 32.0M | append(r, fvs); |
152 | 32.0M | } break; |
153 | 1.88M | case AST_LITERAL_BOOLEAN: { |
154 | 1.88M | assert(dynamic_cast<const LiteralBoolean *>(ast_)); |
155 | | // Nothing to do. |
156 | 1.88M | } break; |
157 | 43.5M | case AST_LITERAL_NUMBER: { |
158 | 43.5M | assert(dynamic_cast<const LiteralNumber *>(ast_)); |
159 | | // Nothing to do. |
160 | 43.5M | } break; |
161 | 118M | case AST_LITERAL_STRING: { |
162 | 118M | assert(dynamic_cast<const LiteralString *>(ast_)); |
163 | | // Nothing to do. |
164 | 118M | } break; |
165 | 118M | case AST_LITERAL_NULL: { |
166 | 1.08M | assert(dynamic_cast<const LiteralNull *>(ast_)); |
167 | | // Nothing to do. |
168 | 1.08M | } break; |
169 | 4.66M | case AST_DESUGARED_OBJECT: { |
170 | 4.66M | assert(dynamic_cast<DesugaredObject *>(ast_)); |
171 | 4.66M | auto* ast = static_cast<DesugaredObject *>(ast_); |
172 | 19.0M | for (auto &field : ast->fields) { |
173 | 19.0M | append(r, static_analysis(field.name, in_object, vars)); |
174 | 19.0M | append(r, static_analysis(field.body, true, vars)); |
175 | 19.0M | } |
176 | 4.66M | for (AST *assert : ast->asserts) { |
177 | 156k | append(r, static_analysis(assert, true, vars)); |
178 | 156k | } |
179 | 4.66M | } break; |
180 | 2.63M | case AST_OBJECT_COMPREHENSION_SIMPLE: { |
181 | 2.63M | assert(dynamic_cast<ObjectComprehensionSimple *>(ast_)); |
182 | 2.63M | auto* ast = static_cast<ObjectComprehensionSimple *>(ast_); |
183 | 2.63M | auto new_vars = vars; |
184 | 2.63M | new_vars.insert(ast->id); |
185 | 2.63M | append(r, static_analysis(ast->field, false, new_vars)); |
186 | 2.63M | append(r, static_analysis(ast->value, true, new_vars)); |
187 | 2.63M | r.erase(ast->id); |
188 | 2.63M | append(r, static_analysis(ast->array, in_object, vars)); |
189 | 2.63M | } break; |
190 | 5.30M | case AST_SELF: { |
191 | 5.30M | assert(dynamic_cast<const Self *>(ast_)); |
192 | 5.30M | if (!in_object) |
193 | 31 | throw StaticError(ast_->location, "Can't use self outside of an object."); |
194 | 5.30M | } break; |
195 | 5.30M | case AST_SUPER_INDEX: { |
196 | 237k | assert(dynamic_cast<const SuperIndex *>(ast_)); |
197 | 237k | auto* ast = static_cast<const SuperIndex *>(ast_); |
198 | 237k | if (!in_object) |
199 | 41 | throw StaticError(ast_->location, "Can't use super outside of an object."); |
200 | 237k | append(r, static_analysis(ast->index, in_object, vars)); |
201 | 237k | } break; |
202 | 2.80M | case AST_UNARY: { |
203 | 2.80M | assert(dynamic_cast<const Unary *>(ast_)); |
204 | 2.80M | auto* ast = static_cast<const Unary *>(ast_); |
205 | 2.80M | append(r, static_analysis(ast->expr, in_object, vars)); |
206 | 2.80M | } break; |
207 | 204M | case AST_VAR: { |
208 | 204M | assert(dynamic_cast<const Var *>(ast_)); |
209 | 204M | auto* ast = static_cast<const Var *>(ast_); |
210 | 204M | if (vars.find(ast->id) == vars.end()) { |
211 | 1.68k | throw StaticError(ast->location, "Unknown variable: " + encode_utf8(ast->id->name)); |
212 | 1.68k | } |
213 | 204M | r.insert(ast->id); |
214 | 204M | } break; |
215 | 0 | default: |
216 | 0 | std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_ << std::endl; |
217 | 0 | std::abort(); |
218 | 0 | break; |
219 | 673M | } |
220 | | |
221 | 673M | for (auto *id : r) |
222 | 923M | ast_->freeVariables.push_back(id); |
223 | | |
224 | 673M | return r; |
225 | 673M | } |
226 | | |
227 | | void jsonnet_static_analysis(AST *ast) |
228 | 21.3k | { |
229 | 21.3k | static_analysis(ast, false, IdSet{}); |
230 | 21.3k | } |
231 | | |
232 | | } // namespace jsonnet::internal |