mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 17:59:39 +00:00
[ddc] Rename variable used to store late value
Late local variables are lowered by the CFE into a local: * backing store variable * get method * set method * isSet local variable (optionally when the type is nullable) This change updates the name in JavaScript used for the backing store variable to match the name for the late variable from the original source. It also updates the scope information passed for expression evaluation to remove the lowered name and replace it with the original so evaluations will work as well. The name change avoids the hiding performed by the debugger on recognized temporary names so it appears in the list of local variables. b/343405209 Issue: See https://github.com/dart-lang/sdk/issues/55918 Change-Id: I6b65a62baf6f26f6e9cfee9f14667d021e16645e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/369506 Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Nicholas Shahan <nshahan@google.com>
This commit is contained in:
parent
8029fdf79a
commit
e7b1bca707
|
@ -4794,11 +4794,17 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
||||||
|
|
||||||
/// Detects temporary variables so we can avoid displaying
|
/// Detects temporary variables so we can avoid displaying
|
||||||
/// them in the debugger if needed.
|
/// them in the debugger if needed.
|
||||||
bool _isTemporaryVariable(VariableDeclaration v) =>
|
bool _isTemporaryVariable(VariableDeclaration v) {
|
||||||
v.isLowered ||
|
// Late local variables are be exposed to the debugger for inspection and
|
||||||
v.isSynthesized ||
|
// evaluation by treating the backing store local variable as a regular
|
||||||
v.name == null ||
|
// non-temporary variable.
|
||||||
v.name!.startsWith('#');
|
// See https://github.com/dart-lang/sdk/issues/55918
|
||||||
|
if (isLateLoweredLocal(v)) return false;
|
||||||
|
return v.isLowered ||
|
||||||
|
v.isSynthesized ||
|
||||||
|
v.name == null ||
|
||||||
|
v.name!.startsWith('#');
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a temporary name recognized by the debugger.
|
/// Creates a temporary name recognized by the debugger.
|
||||||
/// Assumes `_isTemporaryVariable(v)` is true.
|
/// Assumes `_isTemporaryVariable(v)` is true.
|
||||||
|
@ -4821,7 +4827,15 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
||||||
name ??= 't\$${_tempVariables.length}';
|
name ??= 't\$${_tempVariables.length}';
|
||||||
return _tempVariables.putIfAbsent(v, () => _emitTemporaryId(name!));
|
return _tempVariables.putIfAbsent(v, () => _emitTemporaryId(name!));
|
||||||
}
|
}
|
||||||
return _emitIdentifier(v.name!);
|
var name = v.name!;
|
||||||
|
if (isLateLoweredLocal(v)) {
|
||||||
|
// Late local variables are be exposed to the debugger for inspection and
|
||||||
|
// evaluation by treating the backing store local variable as a regular
|
||||||
|
// non-temporary variable.
|
||||||
|
// See https://github.com/dart-lang/sdk/issues/55918
|
||||||
|
name = extractLocalNameFromLateLoweredLocal(name);
|
||||||
|
}
|
||||||
|
return _emitIdentifier(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emits the declaration of a variable.
|
/// Emits the declaration of a variable.
|
||||||
|
|
|
@ -102,6 +102,19 @@ class ExpressionCompiler {
|
||||||
// different from dart.
|
// different from dart.
|
||||||
// See [issue 40273](https://github.com/dart-lang/sdk/issues/40273)
|
// See [issue 40273](https://github.com/dart-lang/sdk/issues/40273)
|
||||||
|
|
||||||
|
// Work around mismatched names and lowered representation for late local
|
||||||
|
// variables.
|
||||||
|
// Replace the existing entries with a name that matches the named
|
||||||
|
// extracted from the lowering.
|
||||||
|
// See https://github.com/dart-lang/sdk/issues/55918
|
||||||
|
var dartLateLocals = [
|
||||||
|
for (var name in dartScope.definitions.keys)
|
||||||
|
if (isLateLoweredLocalName(name)) name,
|
||||||
|
];
|
||||||
|
for (var localName in dartLateLocals) {
|
||||||
|
dartScope.definitions[extractLocalName(localName)] =
|
||||||
|
dartScope.definitions.remove(localName)!;
|
||||||
|
}
|
||||||
// remove undefined js variables (this allows us to get a reference error
|
// remove undefined js variables (this allows us to get a reference error
|
||||||
// from chrome on evaluation)
|
// from chrome on evaluation)
|
||||||
dartScope.definitions.removeWhere((variable, type) =>
|
dartScope.definitions.removeWhere((variable, type) =>
|
||||||
|
|
|
@ -67,6 +67,8 @@ class C {
|
||||||
}
|
}
|
||||||
|
|
||||||
int global = 42;
|
int global = 42;
|
||||||
|
late int lateGlobal;
|
||||||
|
late String lateGlobal2;
|
||||||
|
|
||||||
const soundNullSafety = !(<Null>[] is List<int>);
|
const soundNullSafety = !(<Null>[] is List<int>);
|
||||||
soundNullSafetyTest() {
|
soundNullSafetyTest() {
|
||||||
|
@ -105,6 +107,24 @@ extensionsSymbolTest() {
|
||||||
print(list);
|
print(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lateLocalVariableTest() {
|
||||||
|
late int lateLocal;
|
||||||
|
late int lateLocal2;
|
||||||
|
if (42.isEven) {
|
||||||
|
lateLocal = 42;
|
||||||
|
}
|
||||||
|
// Breakpoint: lateLocalVariableBP
|
||||||
|
print(lateLocal);
|
||||||
|
}
|
||||||
|
|
||||||
|
lateGlobalVariableTest() {
|
||||||
|
if (42.isEven) {
|
||||||
|
lateGlobal = 42;
|
||||||
|
}
|
||||||
|
// Breakpoint: lateGlobalVariableBP
|
||||||
|
print(lateGlobal);
|
||||||
|
}
|
||||||
|
|
||||||
int foo(int x, {int y = 0}) {
|
int foo(int x, {int y = 0}) {
|
||||||
int z = 3;
|
int z = 3;
|
||||||
// Breakpoint: fooBP
|
// Breakpoint: fooBP
|
||||||
|
@ -268,6 +288,8 @@ main() {
|
||||||
soundNullSafetyTest();
|
soundNullSafetyTest();
|
||||||
couldReturnNullTest();
|
couldReturnNullTest();
|
||||||
extensionsSymbolTest();
|
extensionsSymbolTest();
|
||||||
|
lateLocalVariableTest();
|
||||||
|
lateGlobalVariableTest();
|
||||||
|
|
||||||
"1234".parseIntPlusOne();
|
"1234".parseIntPlusOne();
|
||||||
callFooTest();
|
callFooTest();
|
||||||
|
@ -754,6 +776,58 @@ void runNullSafeSharedTests(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('late', () {
|
||||||
|
group('local', () {
|
||||||
|
test(
|
||||||
|
'can be evaluated when initialized',
|
||||||
|
() async {
|
||||||
|
await driver.checkInFrame(
|
||||||
|
breakpointId: 'lateLocalVariableBP',
|
||||||
|
expression: 'lateLocal',
|
||||||
|
expectedResult: '42');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
test('does not throw when evaluated and not initialized', () async {
|
||||||
|
// It isn't clear if this is expected to work or not, the behavior is
|
||||||
|
// somewhat undefined for the debugger. At this time we expose the
|
||||||
|
// backing storage variable that can be displayed or might be null if
|
||||||
|
// uninitialized.
|
||||||
|
// See https://github.com/dart-lang/sdk/issues/55918
|
||||||
|
await driver.checkInFrame(
|
||||||
|
breakpointId: 'lateLocalVariableBP',
|
||||||
|
expression: 'lateLocal2',
|
||||||
|
expectedResult: 'null');
|
||||||
|
});
|
||||||
|
test('throws when not initialized and used in method call', () async {
|
||||||
|
// It isn't clear if this is expected to work or not, the behavior is
|
||||||
|
// somewhat undefined for the debugger. At this time we expose the
|
||||||
|
// backing storage variable that can be displayed or might be null if
|
||||||
|
// uninitialized.
|
||||||
|
// See https://github.com/dart-lang/sdk/issues/55918
|
||||||
|
await driver.checkInFrame(
|
||||||
|
breakpointId: 'lateLocalVariableBP',
|
||||||
|
expression: 'lateLocal2.isEven',
|
||||||
|
expectedError: "Error: Property 'isEven' cannot be accessed on "
|
||||||
|
"'int?' because it is potentially null.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
group('global', () {
|
||||||
|
test('can be evaluated when initialized', () async {
|
||||||
|
await driver.checkInFrame(
|
||||||
|
breakpointId: 'lateGlobalVariableBP',
|
||||||
|
expression: 'lateGlobal',
|
||||||
|
expectedResult: '42');
|
||||||
|
});
|
||||||
|
test('throws when not initialized', () async {
|
||||||
|
await driver.checkInFrame(
|
||||||
|
breakpointId: 'lateGlobalVariableBP',
|
||||||
|
expression: 'lateGlobal2',
|
||||||
|
expectedError: 'Error: LateInitializationError: '
|
||||||
|
"Field 'lateGlobal2' has not been initialized.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
group('regression', () {
|
group('regression', () {
|
||||||
test('don\'t crash on implicit null checks', () async {
|
test('don\'t crash on implicit null checks', () async {
|
||||||
// Compiling an expression that contains a method with a non-nullable
|
// Compiling an expression that contains a method with a non-nullable
|
||||||
|
|
Loading…
Reference in a new issue