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