| 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 |