Skip to content

Commit 91ba697

Browse files
jensjohaCommit Queue
authored and
Commit Queue
committed
[CFE/VM] Fix expression evaluation to not introduce new parameters because of extension types
To support expression evaluation on extension types - which are a static thing and thus doesn't exist in the VM - on the CFE side we try to find the available variables so that, for any variables that are found to be extension types, we can treat them as such. This was introduced in https://dart-review.googlesource.com/c/sdk/+/339900. The problem is that this could accidentally _introduce_ new variables, which would introduce new arguments to the created procedure causing weird behavior in the VM as for instance seen in #56911. This CL makes sure to only set the type for already known variables (as passed by the VM), thus not introducing new variables and fixing the issues seen. Fixes #56911 Change-Id: Ie8b8ae12438338e9c3d248d00cc5da81df5b8ece Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/400120 Reviewed-by: Johnni Winther <[email protected]> Reviewed-by: Derek Xu <[email protected]> Commit-Queue: Jens Johansen <[email protected]>
1 parent 9f111ce commit 91ba697

File tree

5 files changed

+155
-10
lines changed

5 files changed

+155
-10
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -1871,6 +1871,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
18711871
// the extension type.
18721872
for (MapEntry<String, DartType> def
18731873
in foundScope.definitions.entries) {
1874+
if (!usedDefinitions.containsKey(def.key)) continue;
18741875
if (_ExtensionTypeFinder.isOrContainsExtensionType(def.value)) {
18751876
usedDefinitions[def.key] = def.value;
18761877
}

pkg/front_end/test/expression_suite.dart

+56-9
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ import "package:kernel/ast.dart"
4040
Member,
4141
Procedure,
4242
TreeNode,
43-
TypeParameter;
43+
TypeParameter,
44+
VariableDeclaration;
4445
import 'package:kernel/target/targets.dart' show TargetFlags;
4546
import 'package:kernel/text/ast_to_text.dart' show Printer;
4647
import "package:testing/src/log.dart" show splitLines;
@@ -72,7 +73,8 @@ class Context extends ChainContext {
7273
const ReadTest(),
7374
const CompileExpression(),
7475
new MatchProcedureExpectations(".expect",
75-
updateExpectations: updateExpectations)
76+
updateExpectations: updateExpectations),
77+
const OutputParametersMatches(),
7678
];
7779

7880
ProcessedOptions get options => compilerContext.options;
@@ -194,7 +196,51 @@ class TestCase {
194196
}
195197
}
196198

197-
class MatchProcedureExpectations extends Step<List<TestCase>, Null, Context> {
199+
class OutputParametersMatches
200+
extends Step<List<TestCase>, List<TestCase>, Context> {
201+
const OutputParametersMatches();
202+
203+
@override
204+
String get name => "output parameters matches";
205+
206+
@override
207+
Future<Result<List<TestCase>>> run(List<TestCase> tests, Context context) {
208+
for (TestCase test in tests) {
209+
for (CompilationResult result in test.results) {
210+
Procedure? compiledProcedure = result.compiledProcedure;
211+
if (compiledProcedure == null) continue;
212+
if (compiledProcedure.function.namedParameters.isNotEmpty) {
213+
return Future.value(
214+
fail(tests, "Compiled expression contains named parameters."));
215+
}
216+
List<VariableDeclaration> positionals =
217+
compiledProcedure.function.positionalParameters;
218+
if (positionals.length != test.definitions.length) {
219+
return Future.value(fail(
220+
tests,
221+
"Compiled expression contains a wrong number of "
222+
"positional parameters: Expected ${test.definitions.length} "
223+
"(${test.definitions.join(", ")}) "
224+
"but had ${positionals.length} "
225+
"(${positionals.map((p) => p.name).join(", ")})."));
226+
}
227+
for (int i = 0; i < positionals.length; i++) {
228+
if (positionals[i].name != test.definitions[i]) {
229+
return Future.value(fail(
230+
tests,
231+
"Compiled expression doesn't contain '${test.definitions[i]}' "
232+
"but '${positionals[i].name}' as positional parameter $i."));
233+
}
234+
}
235+
}
236+
}
237+
238+
return Future.value(pass(tests));
239+
}
240+
}
241+
242+
class MatchProcedureExpectations
243+
extends Step<List<TestCase>, List<TestCase>, Context> {
198244
final String suffix;
199245
final bool updateExpectations;
200246

@@ -205,7 +251,8 @@ class MatchProcedureExpectations extends Step<List<TestCase>, Null, Context> {
205251
String get name => "match expectations";
206252

207253
@override
208-
Future<Result<Null>> run(List<TestCase> tests, Context context) async {
254+
Future<Result<List<TestCase>>> run(
255+
List<TestCase> tests, Context context) async {
209256
String actual = "";
210257
for (TestCase test in tests) {
211258
String primary = test.results.first.printResult(test.entryPoint, context);
@@ -215,7 +262,7 @@ class MatchProcedureExpectations extends Step<List<TestCase>, Null, Context> {
215262
test.results[i].printResult(test.entryPoint, context);
216263
if (primary != secondary) {
217264
return fail(
218-
null,
265+
tests,
219266
"Multiple expectations don't match on $test:"
220267
"\nFirst expectation:\n$actual\n"
221268
"\nSecond expectation:\n$secondary\n");
@@ -231,19 +278,19 @@ class MatchProcedureExpectations extends Step<List<TestCase>, Null, Context> {
231278
if (!updateExpectations) {
232279
String diff = await runDiff(expectedFile.uri, actual);
233280
return fail(
234-
null, "$testUri doesn't match ${expectedFile.uri}\n$diff");
281+
tests, "$testUri doesn't match ${expectedFile.uri}\n$diff");
235282
}
236283
} else {
237-
return pass(null);
284+
return pass(tests);
238285
}
239286
}
240287
if (updateExpectations) {
241288
await openWrite(expectedFile.uri, (IOSink sink) {
242289
sink.writeln(actual.trim());
243290
});
244-
return pass(null);
291+
return pass(tests);
245292
} else {
246-
return fail(null, """
293+
return fail(tests, """
247294
Please create file ${expectedFile.path} with this content:
248295
$actual""");
249296
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
Errors: {
22
}
3-
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(#lib1::ExtensionType% /* erasure=dart.core::String, declared=! */ input, dart.core::List<#lib1::ExtensionType% /* erasure=dart.core::String, declared=! */> list) → dynamic
3+
static method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr(#lib1::ExtensionType% /* erasure=dart.core::String, declared=! */ input) → dynamic
44
return #lib1::ExtensionType|get#value(input);

pkg/pkg.status

+1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ vm_service/test/issue_25465_test: SkipByDesign # Debugger is disabled in AOT mod
160160
vm_service/test/issue_27238_test: SkipByDesign # Debugger is disabled in AOT mode.
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.
163+
vm_service/test/issue_56911_test: SkipByDesign # Debugger is disabled in AOT mode.
163164
vm_service/test/kill_paused_test: SkipByDesign # Debugger is disabled in AOT mode.
164165
vm_service/test/library_dependency_test: SkipByDesign # Uses 'dart:mirrors' library.
165166
vm_service/test/local_variable_declaration_test: SkipByDesign # Debugger is disabled in AOT mode.
+96
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 = 28;
18+
const LINE_B = 32;
19+
// AUTOGENERATED END
20+
21+
extension type ExtensionType._(String s) {
22+
ExtensionType(int i) : this._('$i');
23+
int get value => s.codeUnitAt(0);
24+
}
25+
26+
void code() {
27+
final list = [ExtensionType(0)];
28+
debugger(); // LINE_A
29+
print(list.single.value);
30+
// ignore: avoid_function_literals_in_foreach_calls
31+
list.forEach((ExtensionType input) {
32+
debugger(); // LINE_B
33+
print(input.value);
34+
});
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+
'list',
74+
], [
75+
('() { return list.single.value; }()', '48'),
76+
('list.single.value', '48'),
77+
]),
78+
resumeIsolate,
79+
hasStoppedAtBreakpoint,
80+
stoppedAtLine(LINE_B),
81+
test('<anonymous closure>', [
82+
'input',
83+
], [
84+
('() { return input.value; }()', '48'),
85+
('input.value', '48'),
86+
]),
87+
];
88+
89+
void main([args = const <String>[]]) => runIsolateTests(
90+
args,
91+
tests,
92+
'issue_56911_test.dart',
93+
testeeConcurrent: code,
94+
pauseOnStart: false,
95+
pauseOnExit: true,
96+
);

0 commit comments

Comments
 (0)