@@ -31,6 +31,34 @@ bool isLateLoweredField(Field node) {
31
31
! node.name.text.endsWith (lateIsSetSuffix);
32
32
}
33
33
34
+ /// Returns the name of the original field for a lowered late field where
35
+ /// [node] is the field holding the value of a lowered late field.
36
+ ///
37
+ /// For instance
38
+ ///
39
+ /// late int field;
40
+ ///
41
+ /// is lowered to (simplified):
42
+ ///
43
+ /// int? _#field = null;
44
+ /// int get field => _#field != null ? _#field : throw 'Uninitialized';
45
+ /// void set field(int value) {
46
+ /// _#field = value;
47
+ /// }
48
+ ///
49
+ /// where '_#field' is the field holding that value and 'field' is the name of
50
+ /// the original field.
51
+ ///
52
+ /// This assumes that `isLateLoweredField(node)` is true.
53
+ Name extractFieldNameFromLateLoweredField (Field node) {
54
+ assert (isLateLoweredField (node));
55
+ String prefix = lateFieldPrefix;
56
+ if (node.isInstanceMember) {
57
+ prefix = '$prefix ${node .enclosingClass .name }#' ;
58
+ }
59
+ return new Name (node.name.text.substring (prefix.length), node.name.library);
60
+ }
61
+
34
62
/// Returns `true` if [node] is the field holding the marker for whether a
35
63
/// lowered late field has been set or not.
36
64
///
@@ -58,6 +86,40 @@ bool isLateLoweredIsSetField(Field node) {
58
86
node.name.text.endsWith (lateIsSetSuffix);
59
87
}
60
88
89
+ /// Returns the name of the original field for a lowered late field where [node]
90
+ /// is the field holding the marker for whether the lowered late field has been
91
+ /// set or not.
92
+ ///
93
+ /// For instance
94
+ ///
95
+ /// late int? field;
96
+ ///
97
+ /// is lowered to (simplified):
98
+ ///
99
+ /// bool _#field#isSet = false;
100
+ /// int? _#field = null;
101
+ /// int get field => _#field#isSet ? _#field : throw 'Uninitialized';
102
+ /// void set field(int value) {
103
+ /// _#field = value;
104
+ /// _#field#isSet = true;
105
+ /// }
106
+ ///
107
+ /// where '_#field#isSet' is the field holding the marker and 'field' is the
108
+ /// name of the original field.
109
+ ///
110
+ /// This assumes that `isLateLoweredIsSetField(node)` is true.
111
+ Name extractFieldNameFromLateLoweredIsSetField (Field node) {
112
+ assert (isLateLoweredIsSetField (node));
113
+ String prefix = lateFieldPrefix;
114
+ if (node.isInstanceMember) {
115
+ prefix = '$prefix ${node .enclosingClass .name }#' ;
116
+ }
117
+ return new Name (
118
+ node.name.text.substring (
119
+ prefix.length, node.name.text.length - lateIsSetSuffix.length),
120
+ node.name.library);
121
+ }
122
+
61
123
/// Returns `true` if [node] is the getter for reading the value of a lowered
62
124
/// late field.
63
125
///
@@ -93,6 +155,30 @@ bool isLateLoweredFieldGetter(Procedure node) {
93
155
return false ;
94
156
}
95
157
158
+ /// Returns the name of the original field for a lowered late field where [node]
159
+ /// is the getter for reading the value of a lowered late field.
160
+ ///
161
+ /// For instance
162
+ ///
163
+ /// late int field;
164
+ ///
165
+ /// is lowered to (simplified):
166
+ ///
167
+ /// int? _#field = null;
168
+ /// int get field => _#field != null ? _#field : throw 'Uninitialized';
169
+ /// void set field(int value) {
170
+ /// _#field = value;
171
+ /// }
172
+ ///
173
+ /// where 'int get field' is the getter for reading the field and 'field' is the
174
+ /// name of the original field.
175
+ ///
176
+ /// This assumes that `isLateLoweredFieldGetter(node)` is true.
177
+ Name extractFieldNameFromLateLoweredFieldGetter (Procedure node) {
178
+ assert (isLateLoweredFieldGetter (node));
179
+ return node.name;
180
+ }
181
+
96
182
/// Returns `true` if [node] is the setter for setting the value of a lowered
97
183
/// late field.
98
184
///
@@ -128,6 +214,265 @@ bool isLateLoweredFieldSetter(Procedure node) {
128
214
return false ;
129
215
}
130
216
217
+ /// Returns the name of the original field for a lowered late field where [node]
218
+ /// is the setter for setting the value of a lowered late field.
219
+ ///
220
+ /// For instance
221
+ ///
222
+ /// late int field;
223
+ ///
224
+ /// is lowered to (simplified):
225
+ ///
226
+ /// int? _#field = null;
227
+ /// int get field => _#field != null ? _#field : throw 'Uninitialized';
228
+ /// void set field(int value) {
229
+ /// _#field = value;
230
+ /// }
231
+ ///
232
+ /// where 'void set field' is the setter for setting the value of the field and
233
+ /// 'field' is the name of the original field.
234
+ ///
235
+ /// This assumes that `isLateLoweredFieldSetter(node)` is true.
236
+ Name extractFieldNameFromLateLoweredFieldSetter (Procedure node) {
237
+ assert (isLateLoweredFieldSetter (node));
238
+ return node.name;
239
+ }
240
+
241
+ /// Returns the original initializer of a lowered late field where [node] is
242
+ /// either the field holding the value, the field holding the marker for whether
243
+ /// it has been set or not, getter for reading the value, or the setter for
244
+ /// setting the value of the field.
245
+ ///
246
+ /// For instance
247
+ ///
248
+ /// late int field = 42;
249
+ ///
250
+ /// is lowered to (simplified):
251
+ ///
252
+ /// int? _#field = null;
253
+ /// int get field => _#field == null ? throw 'Uninitialized' : _#field = 42;
254
+ /// void set field(int value) {
255
+ /// _#field = value;
256
+ /// }
257
+ ///
258
+ /// where this original initializer is `42` , '_#field' is the field holding that
259
+ /// value, '_#field#isSet' is the field holding the marker, 'int get field' is
260
+ /// the getter for reading the field, and 'void set field' is the setter for
261
+ /// setting the value of the field.
262
+ ///
263
+ /// If the original late field had no initializer, `null` is returned.
264
+ ///
265
+ /// If [node] is not part of a late field lowering, `null` is returned.
266
+ Expression getLateFieldInitializer (Member node) {
267
+ Procedure lateFieldGetter = _getLateFieldTarget (node);
268
+ if (lateFieldGetter != null ) {
269
+ Statement body = lateFieldGetter.function.body;
270
+ if (body is Block &&
271
+ body.statements.length == 2 &&
272
+ body.statements.first is IfStatement ) {
273
+ IfStatement ifStatement = body.statements.first;
274
+ if (ifStatement.then is Block ) {
275
+ Block block = ifStatement.then;
276
+ if (block.statements.isNotEmpty &&
277
+ block.statements.first is ExpressionStatement ) {
278
+ ExpressionStatement firstStatement = block.statements.first;
279
+ if (firstStatement.expression is PropertySet ) {
280
+ // We have
281
+ //
282
+ // get field {
283
+ // if (!_#isSet#field) {
284
+ // this._#field = <init>;
285
+ // ...
286
+ // }
287
+ // return _#field;
288
+ // }
289
+ //
290
+ // in case `<init>` is the initializer.
291
+ PropertySet propertySet = firstStatement.expression;
292
+ assert (propertySet.interfaceTarget == getLateFieldTarget (node));
293
+ return propertySet.value;
294
+ } else if (firstStatement.expression is StaticSet ) {
295
+ // We have
296
+ //
297
+ // get field {
298
+ // if (!_#isSet#field) {
299
+ // _#field = <init>;
300
+ // ...
301
+ // }
302
+ // return _#field;
303
+ // }
304
+ //
305
+ // in case `<init>` is the initializer.
306
+ StaticSet staticSet = firstStatement.expression;
307
+ assert (staticSet.target == getLateFieldTarget (node));
308
+ return staticSet.value;
309
+ }
310
+ } else if (block.statements.isNotEmpty &&
311
+ block.statements.first is VariableDeclaration ) {
312
+ // We have
313
+ //
314
+ // get field {
315
+ // if (!_#isSet#field) {
316
+ // var temp = <init>;
317
+ // if (_#isSet#field) throw '...'
318
+ // _#field = temp;
319
+ // _#isSet#field = true
320
+ // }
321
+ // return _#field;
322
+ // }
323
+ //
324
+ // in case `<init>` is the initializer.
325
+ VariableDeclaration variableDeclaration = block.statements.first;
326
+ return variableDeclaration.initializer;
327
+ }
328
+ }
329
+ return null ;
330
+ } else if (body is ReturnStatement ) {
331
+ Expression expression = body.expression;
332
+ if (expression is ConditionalExpression &&
333
+ expression.otherwise is Throw ) {
334
+ // We have
335
+ //
336
+ // get field => _#field#isSet ? #field : throw ...;
337
+ //
338
+ // in which case there is no initializer.
339
+ return null ;
340
+ } else if (expression is Let ) {
341
+ Expression letBody = expression.body;
342
+ if (letBody is ConditionalExpression ) {
343
+ Expression then = letBody.then;
344
+ if (then is Throw ) {
345
+ // We have
346
+ //
347
+ // get field => let # = _#field in <is-unset> ? throw ... : #;
348
+ //
349
+ // in which case there is no initializer.
350
+ return null ;
351
+ } else if (then is PropertySet ) {
352
+ // We have
353
+ //
354
+ // get field => let # = this._#field in <is-unset>
355
+ // ? this._#field = <init> : #;
356
+ //
357
+ // in which case `<init>` is the initializer.
358
+ assert (then.interfaceTarget == getLateFieldTarget (node));
359
+ return then.value;
360
+ } else if (then is StaticSet ) {
361
+ // We have
362
+ //
363
+ // get field => let # = this._#field in <is-unset>
364
+ // ? this._#field = <init> : #;
365
+ //
366
+ // in which case `<init>` is the initializer.
367
+ assert (then.target == getLateFieldTarget (node));
368
+ return then.value;
369
+ } else if (then is Let && then.body is ConditionalExpression ) {
370
+ // We have
371
+ //
372
+ // get field => let #1 = _#field in <is-unset>
373
+ // ? let #2 = <init> in ...
374
+ // : #1;
375
+ //
376
+ // in which case `<init>` is the initializer.
377
+ return then.variable.initializer;
378
+ }
379
+ }
380
+ }
381
+ }
382
+ throw new UnsupportedError (
383
+ 'Unrecognized late getter encoding for $lateFieldGetter : ${body }' );
384
+ }
385
+
386
+ return null ;
387
+ }
388
+
389
+ /// Returns getter for reading the value of a lowered late field where [node] is
390
+ /// either the field holding the value, the field holding the marker for whether
391
+ /// it has been set or not, getter for reading the value, or the setter for
392
+ /// setting the value of the field.
393
+ Procedure _getLateFieldTarget (Member node) {
394
+ Name lateFieldName;
395
+ if (node is Procedure ) {
396
+ if (isLateLoweredFieldGetter (node)) {
397
+ return node;
398
+ } else if (isLateLoweredFieldSetter (node)) {
399
+ lateFieldName = extractFieldNameFromLateLoweredFieldSetter (node);
400
+ }
401
+ } else if (node is Field ) {
402
+ if (isLateLoweredField (node)) {
403
+ lateFieldName = extractFieldNameFromLateLoweredField (node);
404
+ } else if (isLateLoweredIsSetField (node)) {
405
+ lateFieldName = extractFieldNameFromLateLoweredIsSetField (node);
406
+ }
407
+ }
408
+ if (lateFieldName != null ) {
409
+ TreeNode parent = node.parent;
410
+ List <Procedure > procedures;
411
+ if (parent is Class ) {
412
+ procedures = parent.procedures;
413
+ } else if (parent is Library ) {
414
+ procedures = parent.procedures;
415
+ }
416
+ return procedures.singleWhere ((Procedure procedure) =>
417
+ isLateLoweredFieldGetter (procedure) &&
418
+ extractFieldNameFromLateLoweredFieldGetter (procedure) == lateFieldName);
419
+ }
420
+ return null ;
421
+ }
422
+
423
+ /// Returns the field holding the value for a lowered late field where [node] is
424
+ /// either the field holding the value, the field holding the marker for whether
425
+ /// it has been set or not, getter for reading the value, or the setter for
426
+ /// setting the value of the field.
427
+ ///
428
+ /// For instance
429
+ ///
430
+ /// late int field = 42;
431
+ ///
432
+ /// is lowered to (simplified):
433
+ ///
434
+ /// int? _#field = null;
435
+ /// int get field => _#field == null ? throw 'Uninitialized' : _#field = 42;
436
+ /// void set field(int value) {
437
+ /// _#field = value;
438
+ /// }
439
+ ///
440
+ /// where '_#field' is the field holding that value, '_#field#isSet' is the
441
+ /// field holding the marker, 'int get field' is the getter for reading the
442
+ /// field, and 'void set field' is the setter for setting the value of the
443
+ /// field.
444
+ ///
445
+ /// If [node] is not part of a late field lowering, `null` is returned.
446
+ Field getLateFieldTarget (Member node) {
447
+ Name lateFieldName;
448
+ if (node is Procedure ) {
449
+ if (isLateLoweredFieldGetter (node)) {
450
+ lateFieldName = extractFieldNameFromLateLoweredFieldGetter (node);
451
+ } else if (isLateLoweredFieldSetter (node)) {
452
+ lateFieldName = extractFieldNameFromLateLoweredFieldSetter (node);
453
+ }
454
+ } else if (node is Field ) {
455
+ if (isLateLoweredField (node)) {
456
+ return node;
457
+ } else if (isLateLoweredIsSetField (node)) {
458
+ lateFieldName = extractFieldNameFromLateLoweredIsSetField (node);
459
+ }
460
+ }
461
+ if (lateFieldName != null ) {
462
+ TreeNode parent = node.parent;
463
+ List <Field > fields;
464
+ if (parent is Class ) {
465
+ fields = parent.fields;
466
+ } else if (parent is Library ) {
467
+ fields = parent.fields;
468
+ }
469
+ return fields.singleWhere ((Field field) =>
470
+ isLateLoweredField (field) &&
471
+ extractFieldNameFromLateLoweredField (field) == lateFieldName);
472
+ }
473
+ return null ;
474
+ }
475
+
131
476
/// Returns `true` if [node] is the local variable holding the value of a
132
477
/// lowered late variable.
133
478
///
0 commit comments