mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:27:39 +00:00
[dart2js] Implement basic lowering for late instance variables.
This feature is gated behind the --experiment-late-instance-variables flag. Change-Id: I1ecb2d4d960b58204207ea055361463efa3a0bcb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/200922 Commit-Queue: Mayank Patke <fishythefish@google.com> Reviewed-by: Stephen Adams <sra@google.com>
This commit is contained in:
parent
7cdb6444f2
commit
93c96d5857
|
@ -52,6 +52,11 @@ class Flags {
|
|||
|
||||
static const String experimentNewRti = '--experiment-new-rti';
|
||||
|
||||
/// Use the dart2js lowering of late instance variables rather than the CFE
|
||||
/// lowering.
|
||||
static const String experimentLateInstanceVariables =
|
||||
'--experiment-late-instance-variables';
|
||||
|
||||
static const String enableLanguageExperiments = '--enable-experiment';
|
||||
|
||||
static const String fastStartup = '--fast-startup';
|
||||
|
|
|
@ -602,6 +602,7 @@ Future<api.CompilationResult> compile(List<String> argv,
|
|||
new OptionHandler(Flags.experimentUnreachableMethodsThrow, passThrough),
|
||||
new OptionHandler(Flags.experimentCallInstrumentation, passThrough),
|
||||
new OptionHandler(Flags.experimentNewRti, ignoreOption),
|
||||
new OptionHandler(Flags.experimentLateInstanceVariables, passThrough),
|
||||
new OptionHandler('${Flags.mergeFragmentsThreshold}=.+', passThrough),
|
||||
|
||||
// The following three options must come last.
|
||||
|
|
|
@ -17,6 +17,7 @@ import 'package:kernel/reference_from_index.dart';
|
|||
import 'package:kernel/target/changed_structure_notifier.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
|
||||
import '../options.dart';
|
||||
import 'invocation_mirror_constants.dart';
|
||||
import 'transformations/lowering.dart' as lowering show transformLibraries;
|
||||
|
||||
|
@ -77,17 +78,20 @@ class Dart2jsTarget extends Target {
|
|||
@override
|
||||
final String name;
|
||||
|
||||
final bool omitLateNames;
|
||||
final CompilerOptions options;
|
||||
|
||||
Map<String, ir.Class> _nativeClasses;
|
||||
|
||||
Dart2jsTarget(this.name, this.flags, {this.omitLateNames = false});
|
||||
Dart2jsTarget(this.name, this.flags, {this.options});
|
||||
|
||||
@override
|
||||
bool get enableNoSuchMethodForwarders => true;
|
||||
|
||||
@override
|
||||
final int enabledLateLowerings = _enabledLateLowerings;
|
||||
int get enabledLateLowerings =>
|
||||
(options != null && options.experimentLateInstanceVariables)
|
||||
? LateLowering.none
|
||||
: _enabledLateLowerings;
|
||||
|
||||
@override
|
||||
bool get supportsLateLoweringSentinel => true;
|
||||
|
@ -161,8 +165,7 @@ class Dart2jsTarget extends Target {
|
|||
_nativeClasses)
|
||||
.visitLibrary(library);
|
||||
}
|
||||
lowering.transformLibraries(libraries, coreTypes, hierarchy,
|
||||
omitLateNames: omitLateNames);
|
||||
lowering.transformLibraries(libraries, coreTypes, hierarchy, options);
|
||||
logger?.call("Lowering transformations performed");
|
||||
}
|
||||
|
||||
|
|
|
@ -136,8 +136,8 @@ class KernelLoaderTask extends CompilerTask {
|
|||
}
|
||||
} else {
|
||||
bool verbose = false;
|
||||
Target target = Dart2jsTarget(targetName, TargetFlags(),
|
||||
omitLateNames: _options.omitLateNames);
|
||||
Target target =
|
||||
Dart2jsTarget(targetName, TargetFlags(), options: _options);
|
||||
fe.FileSystem fileSystem = CompilerFileSystem(_compilerInput);
|
||||
fe.Verbosity verbosity = _options.verbosity;
|
||||
fe.DiagnosticMessageHandler onDiagnostic =
|
||||
|
|
|
@ -6,16 +6,7 @@ import 'package:kernel/ast.dart';
|
|||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/type_algebra.dart';
|
||||
|
||||
bool _shouldLowerVariable(VariableDeclaration variable) => variable.isLate;
|
||||
|
||||
bool _shouldLowerUninitializedVariable(VariableDeclaration variable) =>
|
||||
_shouldLowerVariable(variable) && variable.initializer == null;
|
||||
|
||||
bool _shouldLowerInitializedVariable(VariableDeclaration variable) =>
|
||||
_shouldLowerVariable(variable) && variable.initializer != null;
|
||||
|
||||
bool _shouldLowerField(Field field) =>
|
||||
field.isLate && field.isStatic && field.initializer == null;
|
||||
import '../../options.dart';
|
||||
|
||||
class _Reader {
|
||||
final Procedure _procedure;
|
||||
|
@ -30,7 +21,8 @@ class _Reader {
|
|||
class LateLowering {
|
||||
final CoreTypes _coreTypes;
|
||||
|
||||
final bool omitLateNames;
|
||||
final bool _omitLateNames;
|
||||
final bool _lowerInstanceVariables;
|
||||
|
||||
final _Reader _readLocal;
|
||||
final _Reader _readField;
|
||||
|
@ -38,15 +30,26 @@ class LateLowering {
|
|||
final _Reader _readInitializedFinal;
|
||||
|
||||
// Each map contains the mapping from late local variables to cells for a
|
||||
// given function scope.
|
||||
// given function scope. All late local variables are lowered to cells.
|
||||
final List<Map<VariableDeclaration, VariableDeclaration>> _variableCells = [];
|
||||
|
||||
// Uninitialized late static fields are lowered to cells.
|
||||
final Map<Field, Field> _fieldCells = {};
|
||||
|
||||
// Late instance fields are lowered to a backing field (plus a getter/setter
|
||||
// pair).
|
||||
final Map<Field, Field> _backingInstanceFields = {};
|
||||
|
||||
// TODO(fishythefish): Remove this when [FieldInitializer] maintains a correct
|
||||
// [Reference] to its [Field].
|
||||
final Map<Procedure, Field> _getterToField = {};
|
||||
|
||||
Member _contextMember;
|
||||
|
||||
LateLowering(this._coreTypes, {this.omitLateNames})
|
||||
: assert(omitLateNames != null),
|
||||
LateLowering(this._coreTypes, CompilerOptions _options)
|
||||
: _omitLateNames = _options?.omitLateNames ?? false,
|
||||
_lowerInstanceVariables =
|
||||
_options?.experimentLateInstanceVariables ?? false,
|
||||
_readLocal = _Reader(_coreTypes.cellReadLocal),
|
||||
_readField = _Reader(_coreTypes.cellReadField),
|
||||
_readInitialized = _Reader(_coreTypes.initializedCellRead),
|
||||
|
@ -54,6 +57,26 @@ class LateLowering {
|
|||
|
||||
Nullability get nonNullable => _contextMember.enclosingLibrary.nonNullable;
|
||||
|
||||
bool _shouldLowerVariable(VariableDeclaration variable) => variable.isLate;
|
||||
|
||||
bool _shouldLowerUninitializedVariable(VariableDeclaration variable) =>
|
||||
_shouldLowerVariable(variable) && variable.initializer == null;
|
||||
|
||||
bool _shouldLowerInitializedVariable(VariableDeclaration variable) =>
|
||||
_shouldLowerVariable(variable) && variable.initializer != null;
|
||||
|
||||
bool _shouldLowerStaticField(Field field) =>
|
||||
field.isLate && field.isStatic && field.initializer == null;
|
||||
|
||||
bool _shouldLowerInstanceField(Field field) =>
|
||||
field.isLate && !field.isStatic && _lowerInstanceVariables;
|
||||
|
||||
String _mangleFieldName(Field field) {
|
||||
assert(_shouldLowerInstanceField(field));
|
||||
Class cls = field.parent;
|
||||
return '_#${cls.name}#${field.name.text}';
|
||||
}
|
||||
|
||||
void transformAdditionalExports(Library library) {
|
||||
List<Reference> additionalExports = library.additionalExports;
|
||||
Set<Reference> newExports = {};
|
||||
|
@ -67,7 +90,7 @@ class LateLowering {
|
|||
}
|
||||
|
||||
ConstructorInvocation _callCellConstructor(Expression name, int fileOffset) =>
|
||||
omitLateNames
|
||||
_omitLateNames
|
||||
? _callCellUnnamedConstructor(fileOffset)
|
||||
: _callCellNamedConstructor(name, fileOffset);
|
||||
|
||||
|
@ -84,7 +107,7 @@ class LateLowering {
|
|||
|
||||
ConstructorInvocation _callInitializedCellConstructor(
|
||||
Expression name, Expression initializer, int fileOffset) =>
|
||||
omitLateNames
|
||||
_omitLateNames
|
||||
? _callInitializedCellUnnamedConstructor(initializer, fileOffset)
|
||||
: _callInitializedCellNamedConstructor(name, initializer, fileOffset);
|
||||
|
||||
|
@ -125,6 +148,11 @@ class LateLowering {
|
|||
interfaceTarget: _setter)
|
||||
..fileOffset = fileOffset;
|
||||
|
||||
StaticInvocation _callIsSentinel(Expression value, int fileOffset) =>
|
||||
StaticInvocation(_coreTypes.isSentinelMethod,
|
||||
Arguments([value])..fileOffset = fileOffset)
|
||||
..fileOffset = fileOffset;
|
||||
|
||||
void enterFunction() {
|
||||
_variableCells.add(null);
|
||||
}
|
||||
|
@ -210,8 +238,7 @@ class LateLowering {
|
|||
return _variableCell(variable);
|
||||
}
|
||||
|
||||
VariableGet _variableCellAccess(
|
||||
VariableDeclaration variable, int fileOffset) {
|
||||
VariableGet _variableCellRead(VariableDeclaration variable, int fileOffset) {
|
||||
assert(_shouldLowerVariable(variable));
|
||||
return VariableGet(_variableCell(variable))..fileOffset = fileOffset;
|
||||
}
|
||||
|
@ -223,7 +250,7 @@ class LateLowering {
|
|||
if (!_shouldLowerVariable(variable)) return node;
|
||||
|
||||
int fileOffset = node.fileOffset;
|
||||
VariableGet cell = _variableCellAccess(variable, fileOffset);
|
||||
VariableGet cell = _variableCellRead(variable, fileOffset);
|
||||
_Reader reader = variable.initializer == null
|
||||
? _readLocal
|
||||
: (variable.isFinal ? _readInitializedFinal : _readInitialized);
|
||||
|
@ -238,7 +265,7 @@ class LateLowering {
|
|||
if (!_shouldLowerVariable(variable)) return node;
|
||||
|
||||
int fileOffset = node.fileOffset;
|
||||
VariableGet cell = _variableCellAccess(variable, fileOffset);
|
||||
VariableGet cell = _variableCellRead(variable, fileOffset);
|
||||
Procedure setter = variable.initializer == null
|
||||
? (variable.isFinal
|
||||
? _coreTypes.cellFinalLocalValueSetter
|
||||
|
@ -250,7 +277,7 @@ class LateLowering {
|
|||
}
|
||||
|
||||
Field _fieldCell(Field field) {
|
||||
assert(_shouldLowerField(field));
|
||||
assert(_shouldLowerStaticField(field));
|
||||
return _fieldCells.putIfAbsent(field, () {
|
||||
int fileOffset = field.fileOffset;
|
||||
Name name = field.name;
|
||||
|
@ -268,43 +295,261 @@ class LateLowering {
|
|||
});
|
||||
}
|
||||
|
||||
StaticGet _fieldCellAccess(Field field, int fileOffset) =>
|
||||
StaticGet(_fieldCell(field))..fileOffset = fileOffset;
|
||||
Field _backingInstanceField(Field field) {
|
||||
assert(_shouldLowerInstanceField(field));
|
||||
return _backingInstanceFields[field] ??=
|
||||
_computeBackingInstanceField(field);
|
||||
}
|
||||
|
||||
Field _computeBackingInstanceField(Field field) {
|
||||
assert(_shouldLowerInstanceField(field));
|
||||
assert(!_backingInstanceFields.containsKey(field));
|
||||
int fileOffset = field.fileOffset;
|
||||
Uri fileUri = field.fileUri;
|
||||
Name name = field.name;
|
||||
String nameText = name.text;
|
||||
DartType type = field.type;
|
||||
Expression initializer = field.initializer;
|
||||
Class enclosingClass = field.enclosingClass;
|
||||
|
||||
Name mangledName = Name(_mangleFieldName(field), field.enclosingLibrary);
|
||||
Field backingField = Field.mutable(mangledName,
|
||||
type: type,
|
||||
initializer: StaticInvocation(_coreTypes.createSentinelMethod,
|
||||
Arguments(const [], types: [type])..fileOffset = fileOffset)
|
||||
..fileOffset = fileOffset,
|
||||
fileUri: fileUri)
|
||||
..fileOffset = fileOffset
|
||||
..isNonNullableByDefault = true
|
||||
..isInternalImplementation = true;
|
||||
InstanceGet fieldRead() => InstanceGet(InstanceAccessKind.Instance,
|
||||
ThisExpression()..fileOffset = fileOffset, mangledName,
|
||||
interfaceTarget: backingField, resultType: type)
|
||||
..fileOffset = fileOffset;
|
||||
InstanceSet fieldWrite(Expression value) => InstanceSet(
|
||||
InstanceAccessKind.Instance,
|
||||
ThisExpression()..fileOffset = fileOffset,
|
||||
mangledName,
|
||||
value,
|
||||
interfaceTarget: backingField)
|
||||
..fileOffset = fileOffset;
|
||||
|
||||
Statement getterBody() {
|
||||
if (initializer == null) {
|
||||
// The lowered getter for `late T field;` and `late final T field;` is
|
||||
//
|
||||
// T get field => _lateReadCheck<T>(this._#field, "field");
|
||||
return ReturnStatement(
|
||||
StaticInvocation(
|
||||
_coreTypes.lateReadCheck,
|
||||
Arguments([fieldRead(), _nameLiteral(nameText, fileOffset)],
|
||||
types: [type])
|
||||
..fileOffset = fileOffset)
|
||||
..fileOffset = fileOffset)
|
||||
..fileOffset = fileOffset;
|
||||
} else if (field.isFinal) {
|
||||
// The lowered getter for `late final T field = e;` is
|
||||
//
|
||||
// T get field {
|
||||
// var value = this._#field;
|
||||
// if (isSentinel(value)) {
|
||||
// final result = e;
|
||||
// _lateInitializeOnceCheck(this._#field, "field");
|
||||
// value = this._#field = result;
|
||||
// }
|
||||
// return value;
|
||||
// }
|
||||
VariableDeclaration value =
|
||||
VariableDeclaration('value', initializer: fieldRead(), type: type)
|
||||
..fileOffset = fileOffset;
|
||||
VariableGet valueRead() => VariableGet(value)..fileOffset = fileOffset;
|
||||
VariableDeclaration result = VariableDeclaration('result',
|
||||
initializer: initializer, type: type, isFinal: true)
|
||||
..fileOffset = fileOffset;
|
||||
VariableGet resultRead() =>
|
||||
VariableGet(result)..fileOffset = fileOffset;
|
||||
return Block([
|
||||
value,
|
||||
IfStatement(
|
||||
_callIsSentinel(valueRead(), fileOffset),
|
||||
Block([
|
||||
result,
|
||||
ExpressionStatement(
|
||||
StaticInvocation(
|
||||
_coreTypes.lateInitializeOnceCheck,
|
||||
Arguments(
|
||||
[fieldRead(), _nameLiteral(nameText, fileOffset)])
|
||||
..fileOffset = fileOffset)
|
||||
..fileOffset = fileOffset)
|
||||
..fileOffset = fileOffset,
|
||||
ExpressionStatement(
|
||||
VariableSet(value, fieldWrite(resultRead()))
|
||||
..fileOffset = fileOffset)
|
||||
..fileOffset = fileOffset
|
||||
])
|
||||
..fileOffset = fileOffset,
|
||||
null)
|
||||
..fileOffset = fileOffset,
|
||||
ReturnStatement(valueRead())..fileOffset = fileOffset
|
||||
])
|
||||
..fileOffset = fileOffset;
|
||||
} else {
|
||||
// The lowered getter for `late T field = e;` is
|
||||
//
|
||||
// T get field {
|
||||
// var value = this._#field;
|
||||
// if (isSentinel(value)) {
|
||||
// value = this._#field = e;
|
||||
// }
|
||||
// return value;
|
||||
// }
|
||||
VariableDeclaration value =
|
||||
VariableDeclaration('value', initializer: fieldRead(), type: type)
|
||||
..fileOffset = fileOffset;
|
||||
VariableGet valueRead() => VariableGet(value)..fileOffset = fileOffset;
|
||||
return Block([
|
||||
value,
|
||||
IfStatement(
|
||||
_callIsSentinel(valueRead(), fileOffset),
|
||||
ExpressionStatement(
|
||||
VariableSet(value, fieldWrite(initializer))
|
||||
..fileOffset = fileOffset)
|
||||
..fileOffset = fileOffset,
|
||||
null)
|
||||
..fileOffset = fileOffset,
|
||||
ReturnStatement(valueRead())..fileOffset = fileOffset
|
||||
])
|
||||
..fileOffset = fileOffset;
|
||||
}
|
||||
}
|
||||
|
||||
Procedure getter = Procedure(name, ProcedureKind.Getter,
|
||||
FunctionNode(getterBody(), returnType: type)..fileOffset = fileOffset,
|
||||
fileUri: fileUri, reference: field.getterReference)
|
||||
..fileOffset = fileOffset
|
||||
..isNonNullableByDefault = true;
|
||||
enclosingClass.addProcedure(getter);
|
||||
_getterToField[getter] = backingField;
|
||||
|
||||
VariableDeclaration setterValue = VariableDeclaration('value', type: type)
|
||||
..fileOffset = fileOffset;
|
||||
VariableGet setterValueRead() =>
|
||||
VariableGet(setterValue)..fileOffset = fileOffset;
|
||||
|
||||
Statement setterBody() {
|
||||
if (!field.isFinal) {
|
||||
// The lowered setter for `late T field;` and `late T field = e;` is
|
||||
//
|
||||
// set field(T value) {
|
||||
// this._#field = value;
|
||||
// }
|
||||
return ExpressionStatement(fieldWrite(setterValueRead()))
|
||||
..fileOffset = fileOffset;
|
||||
} else if (initializer == null) {
|
||||
// The lowered setter for `late final T field;` is
|
||||
//
|
||||
// set field(T value) {
|
||||
// _lateWriteOnceCheck(this._#field, "field");
|
||||
// this._#field = value;
|
||||
// }
|
||||
return Block([
|
||||
ExpressionStatement(
|
||||
StaticInvocation(
|
||||
_coreTypes.lateWriteOnceCheck,
|
||||
Arguments([fieldRead(), _nameLiteral(nameText, fileOffset)])
|
||||
..fileOffset = fileOffset)
|
||||
..fileOffset = fileOffset)
|
||||
..fileOffset = fileOffset,
|
||||
ExpressionStatement(fieldWrite(setterValueRead()))
|
||||
..fileOffset = fileOffset
|
||||
])
|
||||
..fileOffset = fileOffset;
|
||||
} else {
|
||||
// There is no setter for `late final T field = e;`.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Statement body = setterBody();
|
||||
if (body != null) {
|
||||
Procedure setter = Procedure(
|
||||
name,
|
||||
ProcedureKind.Setter,
|
||||
FunctionNode(body,
|
||||
positionalParameters: [setterValue], returnType: VoidType())
|
||||
..fileOffset = fileOffset,
|
||||
fileUri: fileUri,
|
||||
reference: field.setterReference)
|
||||
..fileOffset = fileOffset
|
||||
..isNonNullableByDefault = true;
|
||||
enclosingClass.addProcedure(setter);
|
||||
}
|
||||
|
||||
return backingField;
|
||||
}
|
||||
|
||||
TreeNode transformField(Field field, Member contextMember) {
|
||||
_contextMember = contextMember;
|
||||
|
||||
if (!_shouldLowerField(field)) return field;
|
||||
if (_shouldLowerStaticField(field)) return _fieldCell(field);
|
||||
if (_shouldLowerInstanceField(field)) return _backingInstanceField(field);
|
||||
|
||||
return _fieldCell(field);
|
||||
return field;
|
||||
}
|
||||
|
||||
TreeNode transformFieldInitializer(
|
||||
FieldInitializer initializer, Member contextMember) {
|
||||
_contextMember = contextMember;
|
||||
|
||||
// If the [Field] has been lowered, we can't use `node.field` to retrieve it
|
||||
// because the `getterReference` of the original field now points to the new
|
||||
// getter for the backing field.
|
||||
// TODO(fishythefish): Clean this up when [FieldInitializer] maintains a
|
||||
// correct [Reference] to its [Field].
|
||||
NamedNode node = initializer.fieldReference.node;
|
||||
assert(node != null);
|
||||
Field backingField;
|
||||
if (node is Field) {
|
||||
if (!_shouldLowerInstanceField(node)) return initializer;
|
||||
backingField = _backingInstanceField(node);
|
||||
} else {
|
||||
backingField = _getterToField[node];
|
||||
}
|
||||
assert(backingField != null);
|
||||
|
||||
return FieldInitializer(backingField, initializer.value)
|
||||
..fileOffset = initializer.fileOffset;
|
||||
}
|
||||
|
||||
StaticGet _fieldCellAccess(Field field, int fileOffset) =>
|
||||
StaticGet(_fieldCell(field))..fileOffset = fileOffset;
|
||||
|
||||
TreeNode transformStaticGet(StaticGet node, Member contextMember) {
|
||||
_contextMember = contextMember;
|
||||
|
||||
Member target = node.target;
|
||||
if (target is Field && _shouldLowerField(target)) {
|
||||
if (target is Field && _shouldLowerStaticField(target)) {
|
||||
int fileOffset = node.fileOffset;
|
||||
StaticGet cell = _fieldCellAccess(target, fileOffset);
|
||||
return _callReader(_readField, cell, target.type, fileOffset);
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
TreeNode transformStaticSet(StaticSet node, Member contextMember) {
|
||||
_contextMember = contextMember;
|
||||
|
||||
Member target = node.target;
|
||||
if (target is Field && _shouldLowerField(target)) {
|
||||
if (target is Field && _shouldLowerStaticField(target)) {
|
||||
int fileOffset = node.fileOffset;
|
||||
StaticGet cell = _fieldCellAccess(target, fileOffset);
|
||||
Procedure setter = target.isFinal
|
||||
? _coreTypes.cellFinalFieldValueSetter
|
||||
: _coreTypes.cellValueSetter;
|
||||
return _callSetter(setter, cell, node.value, fileOffset);
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
|
||||
import 'package:kernel/core_types.dart' show CoreTypes;
|
||||
|
||||
import '../../options.dart';
|
||||
import 'factory_specializer.dart';
|
||||
import 'late_lowering.dart';
|
||||
|
||||
|
@ -13,12 +15,9 @@ import 'late_lowering.dart';
|
|||
///
|
||||
/// Each transformation is applied locally to AST nodes of certain types after
|
||||
/// transforming children nodes.
|
||||
void transformLibraries(
|
||||
List<Library> libraries, CoreTypes coreTypes, ClassHierarchy hierarchy,
|
||||
{bool omitLateNames}) {
|
||||
assert(omitLateNames != null);
|
||||
final transformer =
|
||||
_Lowering(coreTypes, hierarchy, omitLateNames: omitLateNames);
|
||||
void transformLibraries(List<Library> libraries, CoreTypes coreTypes,
|
||||
ClassHierarchy hierarchy, CompilerOptions options) {
|
||||
final transformer = _Lowering(coreTypes, hierarchy, options);
|
||||
libraries.forEach(transformer.visitLibrary);
|
||||
|
||||
// Do a second pass to remove/replace now-unused nodes.
|
||||
|
@ -34,10 +33,10 @@ class _Lowering extends Transformer {
|
|||
|
||||
Member _currentMember;
|
||||
|
||||
_Lowering(CoreTypes coreTypes, ClassHierarchy hierarchy, {bool omitLateNames})
|
||||
: assert(omitLateNames != null),
|
||||
factorySpecializer = FactorySpecializer(coreTypes, hierarchy),
|
||||
_lateLowering = LateLowering(coreTypes, omitLateNames: omitLateNames);
|
||||
_Lowering(
|
||||
CoreTypes coreTypes, ClassHierarchy hierarchy, CompilerOptions _options)
|
||||
: factorySpecializer = FactorySpecializer(coreTypes, hierarchy),
|
||||
_lateLowering = LateLowering(coreTypes, _options);
|
||||
|
||||
void transformAdditionalExports(Library node) {
|
||||
_lateLowering.transformAdditionalExports(node);
|
||||
|
@ -88,6 +87,12 @@ class _Lowering extends Transformer {
|
|||
return _lateLowering.transformField(node, _currentMember);
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitFieldInitializer(FieldInitializer node) {
|
||||
node.transformChildren(this);
|
||||
return _lateLowering.transformFieldInitializer(node, _currentMember);
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitStaticGet(StaticGet node) {
|
||||
node.transformChildren(this);
|
||||
|
|
|
@ -400,6 +400,10 @@ class CompilerOptions implements DiagnosticOptions {
|
|||
/// called.
|
||||
bool experimentCallInstrumentation = false;
|
||||
|
||||
/// Use the dart2js lowering of late instance variables rather than the CFE
|
||||
/// lowering.
|
||||
bool experimentLateInstanceVariables = false;
|
||||
|
||||
/// When null-safety is enabled, whether the compiler should emit code with
|
||||
/// unsound or sound semantics.
|
||||
///
|
||||
|
@ -518,6 +522,8 @@ class CompilerOptions implements DiagnosticOptions {
|
|||
_hasOption(options, Flags.experimentUnreachableMethodsThrow)
|
||||
..experimentCallInstrumentation =
|
||||
_hasOption(options, Flags.experimentCallInstrumentation)
|
||||
..experimentLateInstanceVariables =
|
||||
_hasOption(options, Flags.experimentLateInstanceVariables)
|
||||
..generateSourceMap = !_hasOption(options, Flags.noSourceMaps)
|
||||
..outputUri = _extractUriOption(options, '--out=')
|
||||
..platformBinaries = platformBinaries
|
||||
|
|
|
@ -371,6 +371,15 @@ class CoreTypes {
|
|||
late final Procedure initializedCellFinalValueSetter = index.getMember(
|
||||
'dart:_late_helper', '_InitializedCell', 'set:finalValue') as Procedure;
|
||||
|
||||
late final Procedure lateReadCheck =
|
||||
index.getTopLevelProcedure('dart:_late_helper', '_lateReadCheck');
|
||||
|
||||
late final Procedure lateWriteOnceCheck =
|
||||
index.getTopLevelProcedure('dart:_late_helper', '_lateWriteOnceCheck');
|
||||
|
||||
late final Procedure lateInitializeOnceCheck = index.getTopLevelProcedure(
|
||||
'dart:_late_helper', '_lateInitializeOnceCheck');
|
||||
|
||||
InterfaceType get objectLegacyRawType {
|
||||
return _objectLegacyRawType ??= _legacyRawTypes[objectClass] ??=
|
||||
new InterfaceType(objectClass, Nullability.legacy, const <DartType>[]);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
library _late_helper;
|
||||
|
||||
import 'dart:_internal' show LateError;
|
||||
import 'dart:_internal' show LateError, createSentinel, isSentinel;
|
||||
|
||||
void throwLateFieldADI(String fieldName) => throw LateError.fieldADI(fieldName);
|
||||
|
||||
|
@ -109,3 +109,23 @@ class _InitializedCell {
|
|||
_value = v;
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers for lowering late instance fields:
|
||||
// TODO(fishythefish): Support specialization of sentinels based on type.
|
||||
|
||||
@pragma('dart2js:noInline')
|
||||
@pragma('dart2js:as:trust')
|
||||
T _lateReadCheck<T>(Object? value, String name) {
|
||||
if (isSentinel(value)) throw LateError.fieldNI(name);
|
||||
return value as T;
|
||||
}
|
||||
|
||||
@pragma('dart2js:noInline')
|
||||
void _lateWriteOnceCheck(Object? value, String name) {
|
||||
if (!isSentinel(value)) throw LateError.fieldAI(name);
|
||||
}
|
||||
|
||||
@pragma('dart2js:noInline')
|
||||
void _lateInitializeOnceCheck(Object? value, String name) {
|
||||
if (!isSentinel(value)) throw LateError.fieldADI(name);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue