Skip to content

Commit e8ebee8

Browse files
jensjohaCommit Queue
authored andcommitted
[CFE] Expression evaluation will use static type when given 'dynamic'
Fixes #57040 Change-Id: I0f954390c364a91816f66af821e12bee93d4746a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/400302 Commit-Queue: Jens Johansen <[email protected]> Reviewed-by: Johnni Winther <[email protected]> Reviewed-by: Derek Xu <[email protected]>
1 parent 2f33461 commit e8ebee8

File tree

8 files changed

+210
-5
lines changed

8 files changed

+210
-5
lines changed

pkg/front_end/lib/src/base/incremental_compiler.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import 'package:kernel/kernel.dart'
2727
Class,
2828
Component,
2929
DartType,
30+
DynamicType,
3031
Expression,
3132
Extension,
3233
ExtensionType,
@@ -1868,11 +1869,16 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
18681869
library, scriptUriAsUri, cls, offset);
18691870
// For now, if any definition is (or contains) an Extension Type,
18701871
// we'll overwrite the given (runtime?) definitions so we know about
1871-
// the extension type.
1872+
// the extension type. If any definition is said to be dynamic we'll
1873+
// overwrite as well because that mostly means that the value is
1874+
// currently null. This can also mean that the VM can't send over the
1875+
// information - this for instance happens for function types.
18721876
for (MapEntry<String, DartType> def
18731877
in foundScope.definitions.entries) {
1874-
if (!usedDefinitions.containsKey(def.key)) continue;
1875-
if (_ExtensionTypeFinder.isOrContainsExtensionType(def.value)) {
1878+
DartType? existingType = usedDefinitions[def.key];
1879+
if (existingType == null) continue;
1880+
if (existingType is DynamicType ||
1881+
_ExtensionTypeFinder.isOrContainsExtensionType(def.value)) {
18761882
usedDefinitions[def.key] = def.value;
18771883
}
18781884
}

pkg/front_end/testcases/expression/issue_56911_01.expression.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
# https://github.com/dart-lang/sdk/issues/56911
66

77
# Definition, offset, method etc extracted by starting the VM with
8-
# `-DDFE_VERBOSE=false`, e.g.
8+
# `-DDFE_VERBOSE=true`, e.g.
99
# ```
10-
# out/ReleaseX64/dart -DDFE_VERBOSE=false --enable-vm-service \
10+
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
1111
# --serve-observatory --disable-service-auth-codes --pause_isolates_on_start \
1212
# inputFile.dart
1313
# ```
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
# for details. All rights reserved. Use of this source code is governed by a
3+
# BSD-style license that can be found in the LICENSE file.
4+
5+
# https://github.com/dart-lang/sdk/issues/57040
6+
7+
# Definition, offset, method etc extracted by starting the VM with
8+
# `-DDFE_VERBOSE=true`, e.g.
9+
# ```
10+
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
11+
# --serve-observatory --disable-service-auth-codes --pause_isolates_on_start \
12+
# inputFile.dart
13+
# ```
14+
# and then issuing the expression compilation. It will then print stuff like
15+
# `DFE: request[6]: [dart:core, _OneByteString, 1, 0]` in the terminal.
16+
17+
sources: |
18+
import "dart:developer";
19+
20+
extension on String? {
21+
bool get isNullOrEmpty {
22+
var str = this;
23+
return str == null || str.isEmpty;
24+
}
25+
}
26+
27+
void main() {
28+
String? str = "hello";
29+
debugger();
30+
print(str.isNullOrEmpty);
31+
str = null;
32+
debugger();
33+
print(str.isNullOrEmpty);
34+
}
35+
36+
definitions: ["str"]
37+
# String
38+
definition_types: ["dart:core", "_OneByteString", "1", "0"]
39+
type_definitions: []
40+
type_bounds: []
41+
type_defaults: []
42+
method: "main"
43+
static: true
44+
offset: 183 # at the first 'debugger' call.
45+
scriptUri: main.dart
46+
expression: |
47+
str.isNullOrEmpty
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Errors: {
2+
}
3+
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::_OneByteString str) → dynamic
4+
return #lib1::_extension#0|get#isNullOrEmpty(str);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
# for details. All rights reserved. Use of this source code is governed by a
3+
# BSD-style license that can be found in the LICENSE file.
4+
5+
# https://github.com/dart-lang/sdk/issues/57040
6+
7+
# Definition, offset, method etc extracted by starting the VM with
8+
# `-DDFE_VERBOSE=true`, e.g.
9+
# ```
10+
# out/ReleaseX64/dart -DDFE_VERBOSE=true --enable-vm-service \
11+
# --serve-observatory --disable-service-auth-codes --pause_isolates_on_start \
12+
# inputFile.dart
13+
# ```
14+
# and then issuing the expression compilation. It will then print stuff like
15+
# `DFE: request[6]: [dart:core, _OneByteString, 1, 0]` in the terminal.
16+
17+
sources: |
18+
import "dart:developer";
19+
20+
extension on String? {
21+
bool get isNullOrEmpty {
22+
var str = this;
23+
return str == null || str.isEmpty;
24+
}
25+
}
26+
27+
void main() {
28+
String? str = "hello";
29+
debugger();
30+
print(str.isNullOrEmpty);
31+
str = null;
32+
debugger();
33+
print(str.isNullOrEmpty);
34+
}
35+
36+
definitions: ["str"]
37+
# dynamic
38+
definition_types: ["null"]
39+
type_definitions: []
40+
type_bounds: []
41+
type_defaults: []
42+
method: "main"
43+
static: true
44+
offset: 239 # at the second 'debugger' call.
45+
scriptUri: main.dart
46+
expression: |
47+
str.isNullOrEmpty
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Errors: {
2+
}
3+
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(dart.core::String? str) → dynamic
4+
return #lib1::_extension#0|get#isNullOrEmpty(str);

pkg/pkg.status

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ vm_service/test/issue_27238_test: SkipByDesign # Debugger is disabled in AOT mod
161161
vm_service/test/issue_27287_test: SkipByDesign # Debugger is disabled in AOT mode.
162162
vm_service/test/issue_30555_test: SkipByDesign # Debugger is disabled in AOT mode.
163163
vm_service/test/issue_56911_test: SkipByDesign # Debugger is disabled in AOT mode.
164+
vm_service/test/issue_57040_test: SkipByDesign # Debugger is disabled in AOT mode.
164165
vm_service/test/issue_59661_test: SkipByDesign # Debugger is disabled in AOT mode.
165166
vm_service/test/kill_paused_test: SkipByDesign # Debugger is disabled in AOT mode.
166167
vm_service/test/library_dependency_test: SkipByDesign # Uses 'dart:mirrors' library.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:developer';
6+
import 'package:test/test.dart';
7+
import 'package:vm_service/vm_service.dart';
8+
import 'common/service_test_common.dart';
9+
import 'common/test_helper.dart';
10+
11+
// AUTOGENERATED START
12+
//
13+
// Update these constants by running:
14+
//
15+
// dart pkg/vm_service/test/update_line_numbers.dart <test.dart>
16+
//
17+
const LINE_A = 30;
18+
const LINE_B = 33;
19+
// AUTOGENERATED END
20+
21+
extension on String? {
22+
bool get isNullOrEmpty {
23+
final str = this;
24+
return str == null || str.isEmpty;
25+
}
26+
}
27+
28+
void code() {
29+
String? str = 'hello';
30+
debugger(); // LINE_A
31+
print(str.isNullOrEmpty);
32+
str = null;
33+
debugger(); // LINE_B
34+
print(str.isNullOrEmpty);
35+
}
36+
37+
Future<void> Function(VmService, IsolateRef) test(
38+
String topFrameName,
39+
List<String> availableVariables,
40+
List<(String evaluate, String evaluationResult)> evaluations,
41+
) {
42+
return (VmService service, IsolateRef isolateRef) async {
43+
final isolateId = isolateRef.id!;
44+
final stack = await service.getStack(isolateId);
45+
46+
// Make sure we are in the right place.
47+
expect(stack.frames!.length, greaterThanOrEqualTo(1));
48+
expect(stack.frames![0].function!.name, topFrameName);
49+
50+
// Check variables.
51+
expect(
52+
(stack.frames![0].vars ?? []).map((v) => v.name).toList(),
53+
equals(availableVariables),
54+
);
55+
56+
// Evaluate.
57+
for (final (expression, expectedResult) in evaluations) {
58+
final result = await service.evaluateInFrame(
59+
isolateId,
60+
/* frame = */ 0,
61+
expression,
62+
) as InstanceRef;
63+
print(result.valueAsString);
64+
expect(result.valueAsString, equals(expectedResult));
65+
}
66+
};
67+
}
68+
69+
final tests = <IsolateTest>[
70+
hasStoppedAtBreakpoint,
71+
stoppedAtLine(LINE_A),
72+
test('code', [
73+
'str',
74+
], [
75+
('() { return str.isNullOrEmpty; }()', 'false'),
76+
('str.isNullOrEmpty', 'false'),
77+
]),
78+
resumeIsolate,
79+
hasStoppedAtBreakpoint,
80+
stoppedAtLine(LINE_B),
81+
test('code', [
82+
'str',
83+
], [
84+
('() { return str.isNullOrEmpty; }()', 'true'),
85+
('str.isNullOrEmpty', 'true'),
86+
]),
87+
];
88+
89+
void main([args = const <String>[]]) => runIsolateTests(
90+
args,
91+
tests,
92+
'issue_57040_test.dart',
93+
testeeConcurrent: code,
94+
pauseOnStart: false,
95+
pauseOnExit: true,
96+
);

0 commit comments

Comments
 (0)