blob: 027fd3973046dc7d109bbf63eb0c79814709c8b0 [file] [log] [blame]
// 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.
// ignore_for_file: library_private_types_in_public_api
import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart';
import 'common/test_helper.dart';
class _TestClass {
_TestClass();
// Make sure these fields are not removed by the tree shaker.
@pragma('vm:entry-point') // Prevent obfuscation
dynamic x;
@pragma('vm:entry-point') // Prevent obfuscation
dynamic y;
}
_TestClass? target1 = _TestClass();
_TestClass? target2 = _TestClass();
_TestClass? target3 = _TestClass();
_TestClass? target4 = _TestClass();
_TestClass? target5 = _TestClass();
_TestClass? target6 = _TestClass();
_TestClass? target7 = _TestClass();
_TestClass? target8 = _TestClass();
@pragma('vm:entry-point') // Prevent obfuscation
Expando<_TestClass> expando = Expando<_TestClass>();
@pragma('vm:entry-point') // Prevent obfuscation
_TestClass globalObject = _TestClass();
@pragma('vm:entry-point') // Prevent obfuscation
dynamic globalList = List<dynamic>.filled(100, null);
@pragma('vm:entry-point') // Prevent obfuscation
dynamic globalMap1 = {};
@pragma('vm:entry-point') // Prevent obfuscation
dynamic globalMap2 = {};
@pragma('vm:entry-point') // Prevent obfuscation
_TestClass weakReachable = _TestClass();
@pragma('vm:entry-point') // Prevent obfuscation
_TestClass weakUnreachable = _TestClass();
void warmup() {
globalObject.x = target1;
globalObject.y = target2;
globalList[12] = target3;
globalMap1['key'] = target4;
globalMap2[target5] = 'value';
// The weak reference will be traced first in DFS, but the retaining path
// include the strong reference.
weakReachable.x = WeakReference<_TestClass>(target7!);
weakReachable.y = target7;
weakUnreachable.x = WeakReference<_TestClass>(target8!);
weakUnreachable.y = null;
}
@pragma('vm:entry-point') // Prevent obfuscation
_TestClass getGlobalObject() => globalObject;
@pragma('vm:entry-point') // Prevent obfuscation
_TestClass? takeTarget1() {
final tmp = target1;
target1 = null;
return tmp;
}
@pragma('vm:entry-point') // Prevent obfuscation
_TestClass? takeTarget2() {
final tmp = target2;
target2 = null;
return tmp;
}
@pragma('vm:entry-point') // Prevent obfuscation
_TestClass? takeTarget3() {
final tmp = target3;
target3 = null;
return tmp;
}
@pragma('vm:entry-point') // Prevent obfuscation
_TestClass? takeTarget4() {
final tmp = target4;
target4 = null;
return tmp;
}
@pragma('vm:entry-point') // Prevent obfuscation
_TestClass? takeTarget5() {
final tmp = target5;
target5 = null;
return tmp;
}
@pragma('vm:entry-point') // Prevent obfuscation
_TestClass? takeExpandoTarget() {
final tmp = target6;
target6 = null;
final tmp2 = _TestClass();
expando[tmp!] = tmp2;
return tmp2;
}
@pragma('vm:entry-point') // Prevent obfuscation
_TestClass? takeWeakReachableTarget() {
final tmp = target7;
target7 = null;
return tmp;
}
@pragma('vm:entry-point') // Prevent obfuscation
_TestClass? takeWeakUnreachableTarget() {
final tmp = target8;
target8 = null;
return tmp;
}
@pragma('vm:entry-point') // Prevent obfuscation
bool getTrue() => true;
Future<InstanceRef> invoke(String selector) async {
return await rootService.invoke(
isolateId,
isolate.rootLib!.id!,
selector,
[],
) as InstanceRef;
}
late final VmService rootService;
late final Isolate isolate;
late final String isolateId;
final tests = <IsolateTest>[
// Initialization
(VmService service, IsolateRef isolateRef) async {
isolateId = isolateRef.id!;
rootService = service;
isolate = await service.getIsolate(isolateId);
},
// simple path
(VmService service, IsolateRef isolateRef) async {
final obj = await invoke('getGlobalObject');
final result = await service.getRetainingPath(isolateId, obj.id!, 100);
expect(result.gcRootType, 'user global');
expect(result.elements!.length, 2);
expect((result.elements![1].value! as FieldRef).name, 'globalObject');
},
(VmService service, IsolateRef isolateRef) async {
final target = await invoke('takeTarget1');
final result = await service.getRetainingPath(isolateId, target.id!, 100);
expect(result.gcRootType, 'user global');
final elements = result.elements!;
expect(elements.length, 3);
expect(elements[1].parentField, 'x');
expect((elements[2].value as FieldRef).name, 'globalObject');
},
(VmService service, IsolateRef isolateRef) async {
final target = await invoke('takeTarget2');
final result = await service.getRetainingPath(isolateId, target.id!, 100);
expect(result.gcRootType, 'user global');
final elements = result.elements!;
expect(elements.length, 3);
expect(elements[1].parentField, 'y');
expect((elements[2].value as FieldRef).name, 'globalObject');
},
(VmService service, IsolateRef isolateRef) async {
final target = await invoke('takeTarget3');
final result = await service.getRetainingPath(isolateId, target.id!, 100);
expect(result.gcRootType, 'user global');
final elements = result.elements!;
expect(elements.length, 3);
expect(elements[1].parentListIndex, 12);
expect((elements[2].value as FieldRef).name, 'globalList');
},
(VmService service, IsolateRef isolateRef) async {
final target = await invoke('takeTarget4');
final result = await service.getRetainingPath(isolateId, target.id!, 100);
expect(result.gcRootType, 'user global');
final elements = result.elements!;
expect(elements.length, 3);
expect((elements[1].parentMapKey as InstanceRef).valueAsString, 'key');
expect((elements[2].value as FieldRef).name, 'globalMap1');
},
(VmService service, IsolateRef isolateRef) async {
final target = await invoke('takeTarget5');
final result = await service.getRetainingPath(isolateId, target.id!, 100);
expect(result.gcRootType, 'user global');
final elements = result.elements!;
expect(elements.length, 3);
expect(
(elements[1].parentMapKey as InstanceRef).classRef!.name,
'_TestClass',
);
expect((elements[2].value as FieldRef).name, 'globalMap2');
},
(VmService service, IsolateRef isolateRef) async {
// Regression test for https://github.com/dart-lang/sdk/issues/44016
final target = await invoke('takeExpandoTarget');
final result = await service.getRetainingPath(isolateId, target.id!, 100);
final elements = result.elements!;
expect(elements.length, 5);
expect(
(elements[1].parentMapKey as InstanceRef).classRef!.name,
'_TestClass',
);
expect(elements[2].parentListIndex, isNotNull);
expect((elements[4].value as FieldRef).name, 'expando');
},
(VmService service, IsolateRef isolateRef) async {
final target = await invoke('takeWeakReachableTarget');
final result = await service.getRetainingPath(isolateId, target.id!, 100);
expect(result.gcRootType, 'user global');
final elements = result.elements!;
expect(elements.length, 3);
expect(elements[1].parentField, 'y');
expect((elements[2].value as FieldRef).name, 'weakReachable');
},
(VmService service, IsolateRef isolateRef) async {
final target = await invoke('takeWeakUnreachableTarget');
final result = await service.getRetainingPath(isolateId, target.id!, 100);
final elements = result.elements!;
expect(elements.length, 0);
},
// object store
(VmService service, IsolateRef isolateRef) async {
final target = await invoke('getTrue');
final result = await service.getRetainingPath(isolateId, target.id!, 100);
expect(
result.gcRootType == 'isolate_object store' ||
result.gcRootType == 'class table',
true,
);
final elements = result.elements!;
expect(elements.length, 0);
},
];
void main([args = const <String>[]]) async => runIsolateTests(
args,
tests,
'get_retaining_path_rpc_test.dart',
testeeBefore: warmup,
);