Skip to content

Commit e7b15e9

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
[cfe] Add more late lowering helpers
Adds helpers for extracting original name of a lowered late field, the original initializer and the field used to hold the value of the lowered field. This helps backends to customize late field encoding while still using the lowering provided by the cfe. Change-Id: Ib42af539efad84a08a6e32acd29d2efaff129cd7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/178989 Reviewed-by: Jens Johansen <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent 0bfbdfa commit e7b15e9

File tree

10 files changed

+1669
-145
lines changed

10 files changed

+1669
-145
lines changed

pkg/front_end/lib/src/api_prototype/lowering_predicates.dart

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,34 @@ bool isLateLoweredField(Field node) {
3131
!node.name.text.endsWith(lateIsSetSuffix);
3232
}
3333

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+
3462
/// Returns `true` if [node] is the field holding the marker for whether a
3563
/// lowered late field has been set or not.
3664
///
@@ -58,6 +86,40 @@ bool isLateLoweredIsSetField(Field node) {
5886
node.name.text.endsWith(lateIsSetSuffix);
5987
}
6088

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+
61123
/// Returns `true` if [node] is the getter for reading the value of a lowered
62124
/// late field.
63125
///
@@ -93,6 +155,30 @@ bool isLateLoweredFieldGetter(Procedure node) {
93155
return false;
94156
}
95157

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+
96182
/// Returns `true` if [node] is the setter for setting the value of a lowered
97183
/// late field.
98184
///
@@ -128,6 +214,265 @@ bool isLateLoweredFieldSetter(Procedure node) {
128214
return false;
129215
}
130216

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+
131476
/// Returns `true` if [node] is the local variable holding the value of a
132477
/// lowered late variable.
133478
///

pkg/front_end/lib/src/fasta/builder/field_builder.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,7 @@ abstract class AbstractLateFieldEncoding implements FieldEncoding {
902902
if (isSetEncoding == late_lowering.IsSetEncoding.useSentinel) {
903903
_field.initializer = new StaticInvocation(coreTypes.createSentinelMethod,
904904
new Arguments([], types: [_type])..fileOffset = fileOffset)
905+
..fileOffset = fileOffset
905906
..parent = _field;
906907
} else {
907908
_field.initializer = new NullLiteral()

pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6542,6 +6542,7 @@ class InferenceVisitor
65426542
node.initializer = new StaticInvocation(
65436543
inferrer.coreTypes.createSentinelMethod,
65446544
new Arguments([], types: [node.type])..fileOffset = fileOffset)
6545+
..fileOffset = fileOffset
65456546
..parent = node;
65466547
} else {
65476548
node.initializer = null;

0 commit comments

Comments
 (0)