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

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: 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 | tests/compiler/dart2js/js_safety_test.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 JavaScriptExpressionPredicate(js.Expression expression);
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<NativeThrowBehavior> {
asgerf 2015/12/08 11:40:37 Extends BaseVisitor<NativeThrowBehavior> but all t
sra1 2015/12/08 20:54:04 Done.
23 final JavaScriptExpressionPredicate 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,
43 JavaScriptExpressionPredicate isNullableInput) {
44 PlaceholderSafetyAnalysis analysis =
45 new PlaceholderSafetyAnalysis._(isNullableInput);
46 analysis.visit(node);
47 return analysis.maxSafePosition + 1;
48 }
49
50 bool canBeNull(int value) {
51 if (value == NONNULL_VALUE) return false;
52 if (value == UNKNOWN_VALUE) return true;
53 return isNullableInput(value);
54 }
55
56 int unsafe(int value) {
57 safe = false;
58 return value;
59 }
60
61 int visit(js.Node node) {
62 return node.accept(this);
63 }
64
65 int visitNode(js.Node node) {
66 safe = false;
67 print(node);
asgerf 2015/12/08 11:40:37 Renegade print statement
sra1 2015/12/08 20:54:04 Done.
68 super.visitNode(node);
69 return UNKNOWN_VALUE;
70 }
71
72 int visitLiteralNull(js.LiteralNull node) {
73 return UNKNOWN_VALUE;
74 }
75
76 int visitLiteral(js.Literal node) {
77 return NONNULL_VALUE;
78 }
79
80 int handleInterpolatedNode(js.InterpolatedNode node) {
81 assert(node.isPositional);
82 int position = nextPosition++;
83 if (safe) maxSafePosition = position;
84 return position;
85 }
86
87 int visitInterpolatedExpression(js.InterpolatedExpression node) {
88 return handleInterpolatedNode(node);
89 }
90
91 int visitInterpolatedLiteral(js.InterpolatedLiteral node) {
92 return handleInterpolatedNode(node);
93 }
94
95 int visitInterpolatedSelector(js.InterpolatedSelector node) {
96 return handleInterpolatedNode(node);
97 }
98
99 int visitInterpolatedStatement(js.InterpolatedStatement node) {
100 safe = false;
asgerf 2015/12/08 11:40:37 Why safe = false here?
sra1 2015/12/08 20:54:04 Not sure why. It would occur only in the body of a
101 return handleInterpolatedNode(node);
102 }
103
104 int visitInterpolatedDeclaration(js.InterpolatedDeclaration node) {
105 safe = false;
106 return handleInterpolatedNode(node);
107 }
108
109 int visitObjectInitializer(js.ObjectInitializer node) {
110 for (js.Property property in node.properties) {
111 visit(property);
112 }
113 return NONNULL_VALUE;
114 }
115
116 int visitProperty(js.Property node) {
117 visit(node.name);
118 visit(node.value);
119 return UNKNOWN_VALUE;
120 }
121
122 int visitAccess(js.PropertyAccess node) {
123 int first = visit(node.receiver);
124 int second = visit(node.selector);
125 if (canBeNull(first)) safe = false;
asgerf 2015/12/08 11:40:37 If the JS fragment is annotated with throws:never
sra1 2015/12/08 20:54:04 Acknowledged.
126 return UNKNOWN_VALUE;
127 }
128
129 int visitAssignment(js.Assignment node) {
130 js.Expression target = node.leftHandSide;
131
132 int leftToRight() {
133 visit(target);
134 visit(node.value);
135 return UNKNOWN_VALUE;
136 }
137
138 if (target is js.InterpolatedNode) {
139 // A bare interpolated expression should not be the LHS of an assignment.
140 safe = false;
141 return leftToRight();
142 }
143
144 if (node.op == null) {
145 if (target is js.VariableReference) {
146 int value = visit(node.value);
147 // Assignment could change a global or cause a ReferenceError.
148 safe = false;
149 return value;
150 } else if (target is js.PropertyAccess) {
151 // "a.x = b.y" detects a null `b` before a null `a`.
asgerf 2015/12/08 11:40:37 It seems the relevant information is that the RHS
sra1 2015/12/08 20:54:04 I have clarified the comment (I hope).
152 int receiver = visit(target.receiver);
153 int selector = visit(target.selector);
154 int value = visit(node.value);
155 if (canBeNull(receiver)) safe = false;
156 return value;
157 } else {
158 //
asgerf 2015/12/08 11:40:37 Blank comment
sra1 2015/12/08 20:54:04 Done.
159 safe = false;
160 return leftToRight();
161 }
162 }
163 return leftToRight();
164 }
165
166 int visitCall(js.Call node) {
167 visit(node.target);
168 node.arguments.forEach(visit);
169 return unsafe(UNKNOWN_VALUE);
170 }
171
172 int visitNew(js.New node) {
173 // TODO(sra): `new Array(x)` where `x` is a small number.
174 visitCall(node);
175 return unsafe(NONNULL_VALUE);
176 }
177
178 int visitBinary(js.Binary node) {
179 switch (node.op) {
180 // We make the non-conservative assumption that these operations are not
181 // used in ways that force calling arbitrary code via valueOf() or
182 // toString().
183 case "*":
184 case "/":
185 case "%":
186 case "+":
187 case "-":
188 case "<<":
189 case ">>":
190 case ">>>":
191 case "<":
192 case ">":
193 case "<=":
194 case ">=":
195 case "==":
196 case "===":
197 case "!=":
198 case "!==":
199 case "&":
200 case "^":
201 case "|":
202 int left = visit(node.left);
203 int right = visit(node.right);
204 return UNKNOWN_VALUE;
205
206 case ',':
207 int left = visit(node.left);
208 int right = visit(node.right);
209 return right;
210
211 case "&&":
212 case "||":
213 int left = visit(node.left);
214 safe = false;
asgerf 2015/12/08 11:40:37 Just an observation. If there are no placeholders
sra1 2015/12/08 20:54:04 Acknowledged.
215 int right = visit(node.right);
216 return UNKNOWN_VALUE;
217
218 case "instanceof":
219 case "in":
220 int left = visit(node.left);
221 int right = visit(node.right);
222 return UNKNOWN_VALUE;
223
224 default:
225 return unsafe(UNKNOWN_VALUE);
226 }
227 }
228
229 int visitThrow(js.Throw node) {
230 visit(node.expression);
231 return unsafe(UNKNOWN_VALUE);
232 }
233
234 int visitPrefix(js.Prefix node) {
235 if (node.op == 'typeof') {
236 // "typeof a" does not dereference "a".
237 if (node.argument is js.VariableUse) return NONNULL_VALUE;
238 }
239
240 visit(node.argument);
241
242 switch (node.op) {
243 case '+':
244 case '-':
245 case '!':
246 case '~':
247 // Non-conservative assumption that these operators are used on values
248 // that do not call arbitrary code via valueOf() or toString().
249 return NONNULL_VALUE;
asgerf 2015/12/08 11:40:37 The binary operators return UNKNOWN_VALUE. Is ther
sra1 2015/12/08 20:54:04 No reason. They all return numbers, strings, bool
250
251 case 'typeof':
252 return NONNULL_VALUE; // Always a string.
253
254 case 'void':
255 return UNKNOWN_VALUE;
256
257 default:
asgerf 2015/12/08 11:40:37 What about prefix ++ and --?
sra1 2015/12/08 20:54:04 Done.
258 safe = false;
259 return UNKNOWN_VALUE;
260 }
261 }
262
263 int visitPostfix(js.Postfix node) {
264 assert(node.op == '--' || node.op == '++');
265 visit(node.argument);
266 return UNKNOWN_VALUE;
267 }
268
269 int visitVariableUse(js.VariableUse node) {
270 // We could get a ReferenceError unless the variable is in scope. For JS
271 // fragments, the only use of VariableUse outside a `function(){...}` should
272 // be for global references. Certain global names are almost certainly not
273 // reference errors, e.g 'Array'.
asgerf 2015/12/08 11:40:37 I think we could justify adding window and self to
sra1 2015/12/08 20:54:04 Done.
274 switch (node.name) {
275 case 'Array':
276 case 'Date':
277 case 'Function':
278 case 'Object':
279 return NONNULL_VALUE;
280 default:
281 return unsafe(UNKNOWN_VALUE);
282 }
283 }
284
285 int visitFun(js.Fun node) {
286 bool oldSafe = safe;
287 int oldNextPosition = nextPosition;
288 visit(node.body);
289 // Creating a function has no effect on order unless there are embedded
290 // placeholders.
291 safe = (nextPosition == oldNextPosition) && oldSafe;
292 }
293 }
OLDNEW
« no previous file with comments | « no previous file | tests/compiler/dart2js/js_safety_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698