OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2016, 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 /// A library to help transform compounds and null-aware accessors into | |
6 /// let expressions. | |
7 | |
8 import 'package:kernel/ast.dart'; | |
9 | |
10 final Name indexGetName = new Name("[]"); | |
ahe
2017/04/04 09:18:50
We could export these from the kernel version.
Paul Berry
2017/04/04 16:21:10
Done.
Paul Berry
2017/04/04 17:45:20
But for some reason this caused test failures whic
| |
11 | |
12 final Name indexSetName = new Name("[]="); | |
13 | |
14 /// An [Accessor] represents a subexpression for which we can't yet build a | |
15 /// kernel [Expression] because we don't yet know the context in which it is | |
16 /// used. | |
17 /// | |
18 /// Once the context is known, an [Accessor] can be converted into an | |
19 /// [Expression] by calling a "build" method. | |
20 /// | |
21 /// For example, when building a kernel representation for `a[x] = b`, after | |
22 /// parsing `a[x]` but before parsing `= b`, we don't yet know whether to | |
23 /// generate an invocation of `operator[]` or `operator[]=`, so we generate an | |
24 /// [Accessor] object. Later, after `= b` is parsed, [buildAssignment] will be | |
25 /// called. | |
26 abstract class Accessor { | |
27 final int offset; | |
28 | |
29 // [builtBinary] and [builtGetter] capture the inner nodes. Used by | |
30 // dart2js+rasta for determining how subexpressions map to legacy dart2js Ast | |
31 // nodes. This will be removed once dart2js type analysis (aka inference) is | |
32 // reimplemented on kernel. | |
33 Expression builtBinary; | |
34 Expression builtGetter; | |
35 | |
36 Accessor(this.offset); | |
37 | |
38 /// Builds an [Expression] representing a read from the accessor. | |
39 Expression buildSimpleRead() { | |
40 return _finish(_makeSimpleRead()); | |
41 } | |
42 | |
43 /// Builds an [Expression] representing an assignment with the accessor on | |
44 /// the LHS and [value] on the RHS. | |
45 /// | |
46 /// The returned expression evaluates to the assigned value, unless | |
47 /// [voidContext] is true, in which case it may evaluate to anything. | |
48 Expression buildAssignment(Expression value, {bool voidContext: false}) { | |
49 return _finish(_makeSimpleWrite(value, voidContext)); | |
50 } | |
51 | |
52 /// Returns an [Expression] representing a null-aware assignment (`??=`) with | |
53 /// the accessor on the LHS and [value] on the RHS. | |
54 /// | |
55 /// The returned expression evaluates to the assigned value, unless | |
56 /// [voidContext] is true, in which case it may evaluate to anything. | |
57 /// | |
58 /// [type] is the static type of the RHS. | |
59 Expression buildNullAwareAssignment(Expression value, DartType type, | |
60 {bool voidContext: false}) { | |
61 if (voidContext) { | |
62 return _finish(new ConditionalExpression(buildIsNull(_makeRead()), | |
63 _makeWrite(value, false), new NullLiteral(), type)); | |
64 } | |
65 var tmp = new VariableDeclaration.forValue(_makeRead()); | |
66 return _finish(makeLet( | |
67 tmp, | |
68 new ConditionalExpression(buildIsNull(new VariableGet(tmp)), | |
69 _makeWrite(value, false), new VariableGet(tmp), type))); | |
70 } | |
71 | |
72 /// Returns an [Expression] representing a compound assignment (e.g. `+=`) | |
73 /// with the accessor on the LHS and [value] on the RHS. | |
74 Expression buildCompoundAssignment(Name binaryOperator, Expression value, | |
75 {int offset: TreeNode.noOffset, | |
76 bool voidContext: false, | |
77 Procedure interfaceTarget}) { | |
78 return _finish(_makeWrite( | |
79 builtBinary = makeBinary( | |
80 _makeRead(), binaryOperator, interfaceTarget, value, | |
81 offset: offset), | |
82 voidContext)); | |
83 } | |
84 | |
85 /// Returns an [Expression] representing a pre-increment or pre-decrement | |
86 /// of the accessor. | |
87 Expression buildPrefixIncrement(Name binaryOperator, | |
88 {int offset: TreeNode.noOffset, | |
89 bool voidContext: false, | |
90 Procedure interfaceTarget}) { | |
91 return buildCompoundAssignment(binaryOperator, new IntLiteral(1), | |
92 offset: offset, | |
93 voidContext: voidContext, | |
94 interfaceTarget: interfaceTarget); | |
95 } | |
96 | |
97 /// Returns an [Expression] representing a post-increment or post-decrement | |
98 /// of the accessor. | |
99 Expression buildPostfixIncrement(Name binaryOperator, | |
100 {int offset: TreeNode.noOffset, | |
101 bool voidContext: false, | |
102 Procedure interfaceTarget}) { | |
103 if (voidContext) { | |
104 return buildPrefixIncrement(binaryOperator, | |
105 offset: offset, voidContext: true, interfaceTarget: interfaceTarget); | |
106 } | |
107 var value = new VariableDeclaration.forValue(_makeRead()); | |
108 valueAccess() => new VariableGet(value); | |
109 var dummy = new VariableDeclaration.forValue(_makeWrite( | |
110 builtBinary = makeBinary( | |
111 valueAccess(), binaryOperator, interfaceTarget, new IntLiteral(1), | |
112 offset: offset), | |
113 true)); | |
114 return _finish(makeLet(value, makeLet(dummy, valueAccess()))); | |
115 } | |
116 | |
117 Expression _makeSimpleRead() => _makeRead(); | |
118 | |
119 Expression _makeSimpleWrite(Expression value, bool voidContext) { | |
120 return _makeWrite(value, voidContext); | |
121 } | |
122 | |
123 Expression _makeRead(); | |
124 | |
125 Expression _makeWrite(Expression value, bool voidContext); | |
126 | |
127 Expression _finish(Expression body) => body; | |
128 | |
129 /// Returns an [Expression] representing a compile-time error. | |
130 /// | |
131 /// At runtime, an exception will be thrown. | |
132 makeInvalidRead() => new InvalidExpression(); | |
133 | |
134 /// Returns an [Expression] representing a compile-time error wrapping | |
135 /// [value]. | |
136 /// | |
137 /// At runtime, [value] will be evaluated before throwing an exception. | |
138 makeInvalidWrite(Expression value) => wrapInvalid(value); | |
139 } | |
140 | |
141 class VariableAccessor extends Accessor { | |
142 VariableDeclaration variable; | |
143 DartType promotedType; | |
144 | |
145 VariableAccessor(this.variable, this.promotedType, int offset) | |
146 : super(offset); | |
147 | |
148 _makeRead() => new VariableGet(variable, promotedType)..fileOffset = offset; | |
149 | |
150 _makeWrite(Expression value, bool voidContext) { | |
151 return variable.isFinal || variable.isConst | |
152 ? makeInvalidWrite(value) | |
153 : new VariableSet(variable, value)..fileOffset = offset; | |
154 } | |
155 } | |
156 | |
157 class PropertyAccessor extends Accessor { | |
158 VariableDeclaration _receiverVariable; | |
159 Expression receiver; | |
160 Name name; | |
161 Member getter, setter; | |
162 | |
163 static Accessor make( | |
164 Expression receiver, Name name, Member getter, Member setter, | |
165 {int offset: TreeNode.noOffset}) { | |
166 if (receiver is ThisExpression) { | |
167 return new ThisPropertyAccessor(name, getter, setter, offset); | |
168 } else { | |
169 return new PropertyAccessor.internal( | |
170 receiver, name, getter, setter, offset); | |
171 } | |
172 } | |
173 | |
174 PropertyAccessor.internal( | |
175 this.receiver, this.name, this.getter, this.setter, int offset) | |
176 : super(offset); | |
177 | |
178 _makeSimpleRead() => | |
179 new PropertyGet(receiver, name, getter)..fileOffset = offset; | |
180 | |
181 _makeSimpleWrite(Expression value, bool voidContext) { | |
182 return new PropertySet(receiver, name, value, setter)..fileOffset = offset; | |
183 } | |
184 | |
185 receiverAccess() { | |
186 _receiverVariable ??= new VariableDeclaration.forValue(receiver); | |
187 return new VariableGet(_receiverVariable)..fileOffset = offset; | |
188 } | |
189 | |
190 _makeRead() => builtGetter = new PropertyGet(receiverAccess(), name, getter) | |
191 ..fileOffset = offset; | |
192 | |
193 _makeWrite(Expression value, bool voidContext) { | |
194 return new PropertySet(receiverAccess(), name, value, setter) | |
195 ..fileOffset = offset; | |
196 } | |
197 | |
198 _finish(Expression body) => makeLet(_receiverVariable, body); | |
199 } | |
200 | |
201 /// Special case of [PropertyAccessor] to avoid creating an indirect access to | |
202 /// 'this'. | |
203 class ThisPropertyAccessor extends Accessor { | |
204 Name name; | |
205 Member getter, setter; | |
206 | |
207 ThisPropertyAccessor(this.name, this.getter, this.setter, int offset) | |
208 : super(offset); | |
209 | |
210 _makeRead() => builtGetter = | |
211 new PropertyGet(new ThisExpression(), name, getter)..fileOffset = offset; | |
212 | |
213 _makeWrite(Expression value, bool voidContext) { | |
214 return new PropertySet(new ThisExpression(), name, value, setter) | |
215 ..fileOffset = offset; | |
216 } | |
217 } | |
218 | |
219 class NullAwarePropertyAccessor extends Accessor { | |
220 VariableDeclaration receiver; | |
221 Name name; | |
222 Member getter, setter; | |
223 DartType type; | |
224 | |
225 NullAwarePropertyAccessor(Expression receiver, this.name, this.getter, | |
226 this.setter, this.type, int offset) | |
227 : this.receiver = makeOrReuseVariable(receiver), | |
228 super(offset); | |
229 | |
230 receiverAccess() => new VariableGet(receiver); | |
231 | |
232 _makeRead() => builtGetter = new PropertyGet(receiverAccess(), name, getter); | |
233 | |
234 _makeWrite(Expression value, bool voidContext) { | |
235 return new PropertySet(receiverAccess(), name, value, setter); | |
236 } | |
237 | |
238 _finish(Expression body) => makeLet( | |
239 receiver, | |
240 new ConditionalExpression( | |
241 buildIsNull(receiverAccess()), new NullLiteral(), body, type)); | |
242 } | |
243 | |
244 class SuperPropertyAccessor extends Accessor { | |
245 Name name; | |
246 Member getter, setter; | |
247 | |
248 SuperPropertyAccessor(this.name, this.getter, this.setter, int offset) | |
249 : super(offset); | |
250 | |
251 _makeRead() { | |
252 if (getter == null) return makeInvalidRead(); | |
253 // TODO(ahe): Use [DirectPropertyGet] when possible. | |
254 return builtGetter = new SuperPropertyGet(name, getter) | |
255 ..fileOffset = offset; | |
256 } | |
257 | |
258 _makeWrite(Expression value, bool voidContext) { | |
259 if (setter == null) return makeInvalidWrite(value); | |
260 // TODO(ahe): Use [DirectPropertySet] when possible. | |
261 return new SuperPropertySet(name, value, setter)..fileOffset = offset; | |
262 } | |
263 } | |
264 | |
265 class IndexAccessor extends Accessor { | |
266 Expression receiver; | |
267 Expression index; | |
268 VariableDeclaration receiverVariable; | |
269 VariableDeclaration indexVariable; | |
270 Procedure getter, setter; | |
271 | |
272 static Accessor make( | |
273 Expression receiver, Expression index, Procedure getter, Procedure setter, | |
274 {int offset: TreeNode.noOffset}) { | |
275 if (receiver is ThisExpression) { | |
276 return new ThisIndexAccessor(index, getter, setter, offset); | |
277 } else { | |
278 return new IndexAccessor.internal( | |
279 receiver, index, getter, setter, offset); | |
280 } | |
281 } | |
282 | |
283 IndexAccessor.internal( | |
284 this.receiver, this.index, this.getter, this.setter, int offset) | |
285 : super(offset); | |
286 | |
287 _makeSimpleRead() => new MethodInvocation( | |
288 receiver, indexGetName, new Arguments(<Expression>[index]), getter) | |
289 ..fileOffset = offset; | |
290 | |
291 _makeSimpleWrite(Expression value, bool voidContext) { | |
292 if (!voidContext) return _makeWriteAndReturn(value); | |
293 return new MethodInvocation(receiver, indexSetName, | |
294 new Arguments(<Expression>[index, value]), setter)..fileOffset = offset; | |
295 } | |
296 | |
297 receiverAccess() { | |
298 // We cannot reuse the receiver if it is a variable since it might be | |
299 // reassigned in the index expression. | |
300 receiverVariable ??= new VariableDeclaration.forValue(receiver); | |
301 return new VariableGet(receiverVariable)..fileOffset = offset; | |
302 } | |
303 | |
304 indexAccess() { | |
305 indexVariable ??= new VariableDeclaration.forValue(index); | |
306 return new VariableGet(indexVariable)..fileOffset = offset; | |
307 } | |
308 | |
309 _makeRead() { | |
310 return builtGetter = new MethodInvocation( | |
311 receiverAccess(), | |
312 indexGetName, | |
313 new Arguments(<Expression>[indexAccess()]), | |
314 getter)..fileOffset = offset; | |
315 } | |
316 | |
317 _makeWrite(Expression value, bool voidContext) { | |
318 if (!voidContext) return _makeWriteAndReturn(value); | |
319 return new MethodInvocation( | |
320 receiverAccess(), | |
321 indexSetName, | |
322 new Arguments(<Expression>[indexAccess(), value]), | |
323 setter)..fileOffset = offset; | |
324 } | |
325 | |
326 // TODO(dmitryas): remove this method after the "[]=" operator of the Context | |
327 // class is made to return a value. | |
328 _makeWriteAndReturn(Expression value) { | |
329 // The call to []= does not return the value like direct-style assignments | |
330 // do. We need to bind the value in a let. | |
331 var valueVariable = new VariableDeclaration.forValue(value); | |
332 var dummy = new VariableDeclaration.forValue(new MethodInvocation( | |
333 receiverAccess(), | |
334 indexSetName, | |
335 new Arguments( | |
336 <Expression>[indexAccess(), new VariableGet(valueVariable)]), | |
337 setter)..fileOffset = offset); | |
338 return makeLet( | |
339 valueVariable, makeLet(dummy, new VariableGet(valueVariable))); | |
340 } | |
341 | |
342 Expression _finish(Expression body) { | |
343 return makeLet(receiverVariable, makeLet(indexVariable, body)); | |
344 } | |
345 } | |
346 | |
347 /// Special case of [IndexAccessor] to avoid creating an indirect access to | |
348 /// 'this'. | |
349 class ThisIndexAccessor extends Accessor { | |
350 Expression index; | |
351 VariableDeclaration indexVariable; | |
352 Procedure getter, setter; | |
353 | |
354 ThisIndexAccessor(this.index, this.getter, this.setter, int offset) | |
355 : super(offset); | |
356 | |
357 _makeSimpleRead() { | |
358 return new MethodInvocation(new ThisExpression(), indexGetName, | |
359 new Arguments(<Expression>[index]), getter); | |
360 } | |
361 | |
362 _makeSimpleWrite(Expression value, bool voidContext) { | |
363 if (!voidContext) return _makeWriteAndReturn(value); | |
364 return new MethodInvocation(new ThisExpression(), indexSetName, | |
365 new Arguments(<Expression>[index, value]), setter); | |
366 } | |
367 | |
368 indexAccess() { | |
369 indexVariable ??= new VariableDeclaration.forValue(index); | |
370 return new VariableGet(indexVariable); | |
371 } | |
372 | |
373 _makeRead() => builtGetter = new MethodInvocation(new ThisExpression(), | |
374 indexGetName, new Arguments(<Expression>[indexAccess()]), getter); | |
375 | |
376 _makeWrite(Expression value, bool voidContext) { | |
377 if (!voidContext) return _makeWriteAndReturn(value); | |
378 return new MethodInvocation(new ThisExpression(), indexSetName, | |
379 new Arguments(<Expression>[indexAccess(), value]), setter); | |
380 } | |
381 | |
382 _makeWriteAndReturn(Expression value) { | |
383 var valueVariable = new VariableDeclaration.forValue(value); | |
384 var dummy = new VariableDeclaration.forValue(new MethodInvocation( | |
385 new ThisExpression(), | |
386 indexSetName, | |
387 new Arguments( | |
388 <Expression>[indexAccess(), new VariableGet(valueVariable)]), | |
389 setter)); | |
390 return makeLet( | |
391 valueVariable, makeLet(dummy, new VariableGet(valueVariable))); | |
392 } | |
393 | |
394 Expression _finish(Expression body) => makeLet(indexVariable, body); | |
395 } | |
396 | |
397 class SuperIndexAccessor extends Accessor { | |
398 Expression index; | |
399 VariableDeclaration indexVariable; | |
400 Member getter, setter; | |
401 | |
402 SuperIndexAccessor(this.index, this.getter, this.setter, int offset) | |
403 : super(offset); | |
404 | |
405 indexAccess() { | |
406 indexVariable ??= new VariableDeclaration.forValue(index); | |
407 return new VariableGet(indexVariable); | |
408 } | |
409 | |
410 _makeSimpleRead() => new SuperMethodInvocation( | |
411 indexGetName, new Arguments(<Expression>[index]), getter); | |
412 | |
413 _makeSimpleWrite(Expression value, bool voidContext) { | |
414 if (!voidContext) return _makeWriteAndReturn(value); | |
415 return new SuperMethodInvocation( | |
416 indexSetName, new Arguments(<Expression>[index, value]), setter); | |
417 } | |
418 | |
419 _makeRead() { | |
420 return builtGetter = new SuperMethodInvocation( | |
421 indexGetName, new Arguments(<Expression>[indexAccess()]), getter); | |
422 } | |
423 | |
424 _makeWrite(Expression value, bool voidContext) { | |
425 if (!voidContext) return _makeWriteAndReturn(value); | |
426 return new SuperMethodInvocation(indexSetName, | |
427 new Arguments(<Expression>[indexAccess(), value]), setter); | |
428 } | |
429 | |
430 _makeWriteAndReturn(Expression value) { | |
431 var valueVariable = new VariableDeclaration.forValue(value); | |
432 var dummy = new VariableDeclaration.forValue(new SuperMethodInvocation( | |
433 indexSetName, | |
434 new Arguments( | |
435 <Expression>[indexAccess(), new VariableGet(valueVariable)]), | |
436 setter)); | |
437 return makeLet( | |
438 valueVariable, makeLet(dummy, new VariableGet(valueVariable))); | |
439 } | |
440 | |
441 Expression _finish(Expression body) { | |
442 return makeLet(indexVariable, body); | |
443 } | |
444 } | |
445 | |
446 class StaticAccessor extends Accessor { | |
447 Member readTarget; | |
448 Member writeTarget; | |
449 | |
450 StaticAccessor(this.readTarget, this.writeTarget, int offset) : super(offset); | |
451 | |
452 _makeRead() => builtGetter = readTarget == null | |
453 ? makeInvalidRead() | |
454 : new StaticGet(readTarget)..fileOffset = offset; | |
455 | |
456 _makeWrite(Expression value, bool voidContext) { | |
457 return writeTarget == null | |
458 ? makeInvalidWrite(value) | |
459 : new StaticSet(writeTarget, value)..fileOffset = offset; | |
460 } | |
461 } | |
462 | |
463 class ReadOnlyAccessor extends Accessor { | |
464 Expression expression; | |
465 VariableDeclaration value; | |
466 | |
467 ReadOnlyAccessor(this.expression, int offset) : super(offset); | |
468 | |
469 _makeSimpleRead() => expression; | |
470 | |
471 _makeRead() { | |
472 value ??= new VariableDeclaration.forValue(expression); | |
473 return new VariableGet(value); | |
474 } | |
475 | |
476 _makeWrite(Expression value, bool voidContext) => makeInvalidWrite(value); | |
477 | |
478 Expression _finish(Expression body) => makeLet(value, body); | |
479 } | |
480 | |
481 Expression makeLet(VariableDeclaration variable, Expression body) { | |
ahe
2017/04/04 09:18:50
These helper methods could probably also be export
Paul Berry
2017/04/04 16:21:10
I'm going to leave these as is for now because I b
| |
482 if (variable == null) return body; | |
483 return new Let(variable, body); | |
484 } | |
485 | |
486 Expression makeBinary( | |
487 Expression left, Name operator, Procedure interfaceTarget, Expression right, | |
488 {int offset: TreeNode.noOffset}) { | |
489 return new MethodInvocation( | |
490 left, operator, new Arguments(<Expression>[right]), interfaceTarget) | |
491 ..fileOffset = offset; | |
492 } | |
493 | |
494 final Name _equalOperator = new Name('=='); | |
495 | |
496 Expression buildIsNull(Expression value, {int offset: TreeNode.noOffset}) { | |
497 return makeBinary(value, _equalOperator, null, new NullLiteral(), | |
498 offset: offset); | |
499 } | |
500 | |
501 VariableDeclaration makeOrReuseVariable(Expression value) { | |
502 // TODO: Devise a way to remember if a variable declaration was reused | |
503 // or is fresh (hence needs a let binding). | |
504 return new VariableDeclaration.forValue(value); | |
505 } | |
506 | |
507 Expression wrapInvalid(Expression e) { | |
508 return new Let(new VariableDeclaration.forValue(e), new InvalidExpression()); | |
509 } | |
OLD | NEW |