[dartdevc] Adding nnbd semantics for static and late fields.

Fixes #40375

Change-Id: I53863291a8c6a3cc694d088311a9e09b9b00a790
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/138723
Commit-Queue: Mark Zhou <markzipan@google.com>
Reviewed-by: Nicholas Shahan <nshahan@google.com>
This commit is contained in:
Mark Zhou 2020-03-09 19:15:49 +00:00 committed by commit-bot@chromium.org
parent fa2df01895
commit 8fb4645cae
4 changed files with 91 additions and 50 deletions

View file

@ -348,7 +348,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
// emitted before dart.defineLazy.
if (_constLazyAccessors.isNotEmpty) {
var constTableBody = runtimeStatement(
'defineLazy(#, { # })', [_constTable, _constLazyAccessors]);
'defineLazy(#, { # }, false)', [_constTable, _constLazyAccessors]);
moduleItems.insert(_constTableInsertionIndex, constTableBody);
_constLazyAccessors.clear();
}
@ -2106,10 +2106,13 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
}
_staticTypeContext.leaveMember(field);
}
_currentUri = _currentLibrary.fileUri;
_currentUri = savedUri;
return runtimeStatement('defineLazy(#, { # })', [objExpr, accessors]);
return runtimeStatement('defineLazy(#, { # }, #)', [
objExpr,
accessors,
js.boolean(!_currentLibrary.isNonNullableByDefault)
]);
}
js_ast.Fun _emitStaticFieldInitializer(Field field) {

View file

@ -697,7 +697,7 @@ _canonicalMember(obj, name) {
Future loadLibrary() => Future.value();
/// Defines lazy statics.
void defineLazy(to, from) {
void defineLazy(to, from, bool checkCycles) {
for (var name in getOwnNamesAndSymbols(from)) {
defineLazyField(to, name, getOwnPropertyDescriptor(from, name));
}

View file

@ -741,8 +741,89 @@ _canonicalMember(obj, name) {
Future loadLibrary() => Future.value();
/// Defines lazy statics.
void defineLazy(to, from) {
///
/// TODO: Remove useOldSemantics when non-null-safe late static field behavior is
/// deprecated.
void defineLazy(to, from, bool useOldSemantics) {
for (var name in getOwnNamesAndSymbols(from)) {
defineLazyField(to, name, getOwnPropertyDescriptor(from, name));
if (useOldSemantics) {
defineLazyFieldOld(to, name, getOwnPropertyDescriptor(from, name));
} else {
defineLazyField(to, name, getOwnPropertyDescriptor(from, name));
}
}
}
/// Defines a lazy static field.
/// After initial get or set, it will replace itself with a value property.
// TODO(jmesserly): reusing descriptor objects has been shown to improve
// performance in other projects (e.g. webcomponents.js ShadowDOM polyfill).
defineLazyField(to, name, desc) => JS('', '''(() => {
const initializer = $desc.get;
let init = initializer;
let value = null;
let executed = false;
$desc.get = function() {
if (init == null) return value;
if (!executed) {
// Record the field on first execution so we can reset it later if
// needed (hot restart).
$_resetFields.push(() => {
init = initializer;
value = null;
});
executed = true;
}
value = init();
init = null;
return value;
};
$desc.configurable = true;
if ($desc.set != null) {
$desc.set = function(x) {
init = null;
value = x;
};
}
return ${defineProperty(to, name, desc)};
})()''');
/// Defines a lazy static field with pre-null-safety semantics.
defineLazyFieldOld(to, name, desc) => JS('', '''(() => {
const initializer = $desc.get;
let init = initializer;
let value = null;
$desc.get = function() {
if (init == null) return value;
let f = init;
init = $throwCyclicInitializationError;
if (f === init) f($name); // throw cycle error
// On the first (non-cyclic) execution, record the field so we can reset it
// later if needed (hot restart).
$_resetFields.push(() => {
init = initializer;
value = null;
});
// Try to evaluate the field, using try+catch to ensure we implement the
// correct Dart error semantics.
try {
value = f();
init = null;
return value;
} catch (e) {
init = null;
value = null;
throw e;
}
};
$desc.configurable = true;
if ($desc.set != null) {
$desc.set = function(x) {
init = null;
value = x;
};
}
return ${defineProperty(to, name, desc)};
})()''');

View file

@ -60,49 +60,6 @@ safeGetOwnProperty(obj, name) {
return JS<Object>('', '#[#]', obj, name);
}
/// Defines a lazy static field.
/// After initial get or set, it will replace itself with a value property.
// TODO(jmesserly): reusing descriptor objects has been shown to improve
// performance in other projects (e.g. webcomponents.js ShadowDOM polyfill).
defineLazyField(to, name, desc) => JS('', '''(() => {
const initializer = $desc.get;
let init = initializer;
let value = null;
$desc.get = function() {
if (init == null) return value;
let f = init;
init = $throwCyclicInitializationError;
if (f === init) f($name); // throw cycle error
// On the first (non-cyclic) execution, record the field so we can reset it
// later if needed (hot restart).
$_resetFields.push(() => {
init = initializer;
value = null;
});
// Try to evaluate the field, using try+catch to ensure we implement the
// correct Dart error semantics.
try {
value = f();
init = null;
return value;
} catch (e) {
init = null;
value = null;
throw e;
}
};
$desc.configurable = true;
if ($desc.set != null) {
$desc.set = function(x) {
init = null;
value = x;
};
}
return ${defineProperty(to, name, desc)};
})()''');
copyTheseProperties(to, from, names) {
for (int i = 0, n = JS('!', '#.length', names); i < n; ++i) {
var name = JS('', '#[#]', names, i);