Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(74)

Side by Side Diff: pkg/compiler/lib/src/js/placeholder_safety.dart

Issue 1510633003: Safety analysis for JS template placeholders. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Make predicate positional Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5
6 library js.safety;
7
8 import "js.dart" as js;
9
10 typedef bool PositionPredicate(int position);
11
12 /// PlaceholderSafetyAnalysis determines which placeholders in a JavaScript
13 /// template may be replaced with an arbitrary expression. Placeholders may be
14 /// replaced with an arbitrary expression providied the template ensures the
15 /// placeholders are evaluated in the same left-to-right order with no
16 /// additional effects interleaved.
17 ///
18 /// The result is semi-conservative, giving reasonable results for many simple
19 /// JS fragments. The non-conservative part is the assumption that arithmetic
20 /// operators are used on 'good' operands that do not force arbitrary code to be
21 /// executed via conversions (valueOf() and toString() methods).
22 class PlaceholderSafetyAnalysis extends js.BaseVisitor<int> {
23 final PositionPredicate isNullableInput;
24 int nextPosition = 0;
25 int maxSafePosition = -1;
26 bool safe = true;
27
28 // We do a crude abstract interpretation to find operations that might throw
29 // exceptions. The possible values of expressions are represented by
30 // integers. Small non-negative integers 0, 1, 2, ... represent the values of
31 // the placeholders. Other values are:
32 static const int NONNULL_VALUE = -1; // Unknown but not null.
33 static const int UNKNOWN_VALUE = -2; // Unknown and might be null.
34
35 PlaceholderSafetyAnalysis._(this.isNullableInput);
36
37 /// Returns the number of placeholders that can be substituted into the
38 /// template AST [node] without changing the order of observable effects.
39 /// [isNullableInput] is a function that takes the 0-based index of a
40 /// placeholder and returns `true` if expression at run time may be null, and
41 /// `false` if the value is never null.
42 static int analyze(js.Node node, PositionPredicate isNullableInput) {
43 PlaceholderSafetyAnalysis analysis =
44 new PlaceholderSafetyAnalysis._(isNullableInput);
45 analysis.visit(node);
46 return analysis.maxSafePosition + 1;
47 }
48
49 bool canBeNull(int value) {
50 if (value == NONNULL_VALUE) return false;
51 if (value == UNKNOWN_VALUE) return true;
52 return isNullableInput(value);
53 }
54
55 int unsafe(int value) {
56 safe = false;
57 return value;
58 }
59
60 int visit(js.Node node) {
61 return node.accept(this);
62 }
63
64 int visitNode(js.Node node) {
65 safe = false;
66 super.visitNode(node);
67 return UNKNOWN_VALUE;
68 }
69
70 int visitLiteralNull(js.LiteralNull node) {
71 return UNKNOWN_VALUE;
72 }
73
74 int visitLiteral(js.Literal node) {
75 return NONNULL_VALUE;
76 }
77
78 int handleInterpolatedNode(js.InterpolatedNode node) {
79 assert(node.isPositional);
80 int position = nextPosition++;
81 if (safe) maxSafePosition = position;
82 return position;
83 }
84
85 int visitInterpolatedExpression(js.InterpolatedExpression node) {
86 return handleInterpolatedNode(node);
87 }
88
89 int visitInterpolatedLiteral(js.InterpolatedLiteral node) {
90 return handleInterpolatedNode(node);
91 }
92
93 int visitInterpolatedSelector(js.InterpolatedSelector node) {
94 return handleInterpolatedNode(node);
95 }
96
97 int visitInterpolatedStatement(js.InterpolatedStatement node) {
98 return handleInterpolatedNode(node);
99 }
100
101 int visitInterpolatedDeclaration(js.InterpolatedDeclaration node) {
102 return handleInterpolatedNode(node);
103 }
104
105 int visitObjectInitializer(js.ObjectInitializer node) {
106 for (js.Property property in node.properties) {
107 visit(property);
108 }
109 return NONNULL_VALUE;
110 }
111
112 int visitProperty(js.Property node) {
113 visit(node.name);
114 visit(node.value);
115 return UNKNOWN_VALUE;
116 }
117
118 int visitArrayInitializer(js.ArrayInitializer node) {
119 node.elements.forEach(visit);
120 return NONNULL_VALUE;
121 }
122
123 int visitArrayHole(js.ArrayHole node) {
124 return UNKNOWN_VALUE;
125 }
126
127 int visitAccess(js.PropertyAccess node) {
128 int first = visit(node.receiver);
129 int second = visit(node.selector);
130 // TODO(sra): If the JS is annotated as never throwing, we can avoid this.
131 if (canBeNull(first)) safe = false;
132 return UNKNOWN_VALUE;
133 }
134
135 int visitAssignment(js.Assignment node) {
136 js.Expression left = node.leftHandSide;
137 js.Expression right = node.value;
138
139 int leftToRight() {
140 visit(left);
141 visit(right);
142 return UNKNOWN_VALUE;
143 }
144
145 if (left is js.InterpolatedNode) {
146 // A bare interpolated expression should not be the LHS of an assignment.
147 safe = false;
148 return leftToRight();
149 }
150
151 // Assignment operators dereference the LHS before evaluating the RHS.
152 if (node.op != null) return leftToRight();
153
154 // Assignment (1) evaluates the LHS as a Reference `lval`, (2) evaluates the
155 // RHS as a value, (3) dereferences the `lval` in PutValue.
156 if (left is js.VariableReference) {
157 int value = visit(right);
158 // Assignment could change an observed global or cause a ReferenceError.
159 safe = false;
160 return value;
161 }
162 if (left is js.PropertyAccess) {
163 // "a.b.x = c.y" gives a TypeError for null values in this order: `a`,
164 // `c`, `a.b`.
165 int receiver = visit(left.receiver);
166 int selector = visit(left.selector);
167 int value = visit(right);
168 if (canBeNull(receiver)) safe = false;
169 return value;
170 }
171 // Be conserative with unrecognized LHS expressions.
172 safe = false;
173 return leftToRight();
174 }
175
176 int visitCall(js.Call node) {
177 // TODO(sra): Recognize JavaScript built-ins like
178 // 'Object.prototype.hasOwnProperty.call'.
179 visit(node.target);
180 node.arguments.forEach(visit);
181 return unsafe(UNKNOWN_VALUE);
182 }
183
184 int visitNew(js.New node) {
185 visit(node.target);
186 node.arguments.forEach(visit);
187 return unsafe(NONNULL_VALUE);
188 }
189
190 int visitBinary(js.Binary node) {
191 switch (node.op) {
192 // We make the non-conservative assumption that these operations are not
193 // used in ways that force calling arbitrary code via valueOf() or
194 // toString().
195 case "*":
196 case "/":
197 case "%":
198 case "+":
199 case "-":
200 case "<<":
201 case ">>":
202 case ">>>":
203 case "<":
204 case ">":
205 case "<=":
206 case ">=":
207 case "==":
208 case "===":
209 case "!=":
210 case "!==":
211 case "&":
212 case "^":
213 case "|":
214 int left = visit(node.left);
215 int right = visit(node.right);
216 return NONNULL_VALUE; // Number, String, Boolean.
217
218 case ',':
219 int left = visit(node.left);
220 int right = visit(node.right);
221 return right;
222
223 case "&&":
224 case "||":
225 int left = visit(node.left);
226 // TODO(sra): Might be safe, e.g. "x || 0".
227 safe = false;
228 int right = visit(node.right);
229 return UNKNOWN_VALUE;
230
231 case "instanceof":
232 case "in":
233 int left = visit(node.left);
234 int right = visit(node.right);
235 return UNKNOWN_VALUE;
236
237 default:
238 return unsafe(UNKNOWN_VALUE);
239 }
240 }
241
242 int visitConditional(js.Conditional node) {
243 int cond = visit(node.condition);
244 // TODO(sra): Might be safe, e.g. "# ? 1 : 2".
245 safe = false;
246 int thenValue = visit(node.then);
247 int elseValue = visit(node.otherwise);
248 return UNKNOWN_VALUE;
249 }
250
251 int visitThrow(js.Throw node) {
252 visit(node.expression);
253 return unsafe(UNKNOWN_VALUE);
254 }
255
256 int visitPrefix(js.Prefix node) {
257 if (node.op == 'typeof') {
258 // "typeof a" first evaluates to a Reference. If the Reference is to a
259 // variable that is not present, "undefined" is returned without
260 // dereferencing.
261 if (node.argument is js.VariableUse) return NONNULL_VALUE; // A string.
262 }
263
264 visit(node.argument);
265
266 switch (node.op) {
267 case '+':
268 case '-':
269 case '!':
270 case '~':
271 // Non-conservative assumption that these operators are used on values
272 // that do not call arbitrary code via valueOf() or toString().
273 return NONNULL_VALUE;
274
275 case 'typeof':
276 return NONNULL_VALUE; // Always a string.
277
278 case 'void':
279 return UNKNOWN_VALUE;
280
281 case '--':
282 case '++':
283 return NONNULL_VALUE; // Always a number.
284
285 default:
286 safe = false;
287 return UNKNOWN_VALUE;
288 }
289 }
290
291 int visitPostfix(js.Postfix node) {
292 assert(node.op == '--' || node.op == '++');
293 visit(node.argument);
294 return NONNULL_VALUE; // Always a number, even for "(a=null, a++)".
295 }
296
297 int visitVariableUse(js.VariableUse node) {
298 // We could get a ReferenceError unless the variable is in scope. For JS
299 // fragments, the only use of VariableUse outside a `function(){...}` should
300 // be for global references. Certain global names are almost certainly not
301 // reference errors, e.g 'Array'.
302 switch (node.name) {
303 case 'Array':
304 case 'Date':
305 case 'Function':
306 case 'Number':
307 case 'Object':
308 case 'RegExp':
309 case 'String':
310 case 'self':
311 case 'window':
312 return NONNULL_VALUE;
313 default:
314 return unsafe(UNKNOWN_VALUE);
315 }
316 }
317
318 int visitFun(js.Fun node) {
319 bool oldSafe = safe;
320 int oldNextPosition = nextPosition;
321 visit(node.body);
322 // Creating a function has no effect on order unless there are embedded
323 // placeholders.
324 safe = (nextPosition == oldNextPosition) && oldSafe;
325 return NONNULL_VALUE;
326 }
327 }
OLDNEW
« no previous file with comments | « no previous file | pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698