dart-sdk/pkg/vm_service/test/evaluate_inside_closures_test.dart
Martin Kustermann 587e6308bf [vm] Fix expression evaluation in context of closures
When evaluating an expressino at a breakpoint that's inside a closure,
the closure may refer to anything it closes over. That includes the
`this` from an enclosing method.

So depending on whether a closure's parent chain is an instance method
or a static method, the expression evaluation function is going to be an
instance method or a static method.

=> We walk up the parent chain to determine the correct enclosing class.
=> This avoids making a Type object with a cid from a top-level class (see [0])

Handling this correctly will now try to get the `this` when invoking an
eval function that has an enclosing instance method. Though we may often
have "<optimized out>" the `this` (e.g. due to not capturing it in
closure context chain).

=> We still allow running the expression evaluation function in this
case, but only if the expression being evaluated doesn't access `this`.

A similar issue occurs when trying to use variables in the eval
expression that the closure didn't capture. This results in a confusing
CFE compile-time error. This is a separate issue and tracked in [1].

=> We update the test to distinuish the cases that this CL makes passing
and those that are failing due to [1].

Fixes [0] https://github.com/dart-lang/sdk/issues/53061
See also [1] https://github.com/dart-lang/sdk/issues/53087

TEST=Fixes part of service/evaluate_activation_test failures

Change-Id: I3bb24e7338c7b2f12d5340311d944cb59a455641
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/317540
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
2023-08-04 11:07:46 +00:00

96 lines
2.8 KiB
Dart

// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Regression test for https://dartbug.com/52430.
import 'dart:developer';
import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart';
import 'common/service_test_common.dart';
import 'common/test_helper.dart';
class C {
static int staticField = 12;
int instanceField = 34;
static void staticMethod() {
((int x) {
debugger();
})(56);
}
void instanceMethod() {
((int y) {
debugger();
use(this);
})(78);
}
}
void use(_) {}
testMain() {
C c = C();
C.staticMethod();
c.instanceMethod();
}
final tests = <IsolateTest>[
hasStoppedAtBreakpoint,
stoppedAtLine(21),
(VmService service, IsolateRef isolateRef) async {
final isolateId = isolateRef.id!;
final xRef =
await service.evaluateInFrame(isolateId, 0, 'x') as InstanceRef;
expect(xRef.valueAsString, '56');
InstanceRef staticFieldRef = await service.evaluateInFrame(
isolateId, 0, 'staticField += 1') as InstanceRef;
expect(staticFieldRef.valueAsString, '13');
staticFieldRef = await service.evaluateInFrame(isolateId, 0, 'staticField')
as InstanceRef;
expect(staticFieldRef.valueAsString, '13');
// Evaluating 'instanceField' should fail since we are paused in the context
// of a static method.
try {
await service.evaluateInFrame(isolateId, 0, 'instanceField');
fail('Expected RPCError');
} catch (e) {
final rpcError = e as RPCError;
expect(rpcError.code, RPCErrorKind.kExpressionCompilationError.code);
}
},
resumeIsolate,
stoppedAtLine(27),
(VmService service, IsolateRef isolateRef) async {
final isolateId = isolateRef.id!;
final yRef =
await service.evaluateInFrame(isolateId, 0, 'y') as InstanceRef;
expect(yRef.valueAsString, '78');
InstanceRef staticFieldRef = await service.evaluateInFrame(
isolateId, 0, 'staticField += 1') as InstanceRef;
expect(staticFieldRef.valueAsString, '14');
staticFieldRef = await service.evaluateInFrame(isolateId, 0, 'staticField')
as InstanceRef;
expect(staticFieldRef.valueAsString, '14');
InstanceRef instanceFieldRef = await service.evaluateInFrame(
isolateId, 0, 'instanceField += 1') as InstanceRef;
expect(instanceFieldRef.valueAsString, '35');
instanceFieldRef = await service.evaluateInFrame(
isolateId, 0, 'instanceField') as InstanceRef;
expect(instanceFieldRef.valueAsString, '35');
}
];
main([args = const <String>[]]) async =>
runIsolateTests(args, tests, 'evaluate_inside_closures_test.dart',
testeeConcurrent: testMain);