Skip to content

Don't fetch range if the object has no length #1453

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dwds/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

- Fix chrome detection in iPhone emulation mode in chrome or edge browsers.
- Reliably find unused port for extension backend http service.
- Ignore offset / count parameters in getObject if the object has no length

## 11.4.0

Expand Down
7 changes: 4 additions & 3 deletions dwds/lib/src/debugging/debugger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@ class Debugger extends Domain {
/// List/Map and [offset] and [count] should indicate the desired range.
Future<RemoteObject> _subrange(
String id, int offset, int count, int length) async {
assert(offset != null);
assert(length != null);
// TODO(#809): Sometimes we already know the type of the object, and
// we could take advantage of that to short-circuit.
var receiver = remoteObjectFor(id);
Expand Down Expand Up @@ -395,12 +397,11 @@ class Debugger extends Domain {
/// Note that the property names are JS names, e.g.
/// Symbol(DartClass.actualName) and will need to be converted. For a system
/// List or Map, [offset] and/or [count] can be provided to indicate a desired
/// range of entries. If those are provided, then [length] should also be
/// provided to indicate the total length of the List/Map.
/// range of entries. They will be ignored if there is no [length].
Future<List<Property>> getProperties(String objectId,
{int offset, int count, int length}) async {
var rangeId = objectId;
if (offset != null || count != null) {
if (length != null && (offset != null || count != null)) {
var range = await _subrange(objectId, offset ?? 0, count, length);
rangeId = range.objectId;
}
Expand Down
257 changes: 184 additions & 73 deletions dwds/test/chrome_proxy_service_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -492,28 +492,6 @@ void main() {
expect(world.valueAsString, 'world');
});

test('Strings with offset', () async {
var worldRef = await service.evaluate(
isolate.id, bootstrap.id, "helloString('world')") as InstanceRef;
var world = await service.getObject(isolate.id, worldRef.id,
count: 2, offset: 1) as Instance;
expect(world.valueAsString, 'or');
expect(world.count, 2);
expect(world.length, 5);
expect(world.offset, 1);
});

test('Strings with offset off the end', () async {
var worldRef = await service.evaluate(
isolate.id, bootstrap.id, "helloString('world')") as InstanceRef;
var world = await service.getObject(isolate.id, worldRef.id,
count: 5, offset: 3) as Instance;
expect(world.valueAsString, 'ld');
expect(world.count, 2);
expect(world.length, 5);
expect(world.offset, 3);
});

test('Large strings not truncated', () async {
var largeString = await service.evaluate(
isolate.id, bootstrap.id, "helloString('${'abcde' * 250}')")
Expand Down Expand Up @@ -564,30 +542,6 @@ void main() {
expect(sixth.valueAsString, '5');
});

test('Lists with count/offset', () async {
var list = await createList();
var inst = await service.getObject(isolate.id, list.objectId,
count: 7, offset: 4) as Instance;
expect(inst.length, 1001);
expect(inst.offset, 4);
expect(inst.count, 7);
var fifth = inst.elements[0] as InstanceRef;
expect(fifth.valueAsString, '100');
var sixth = inst.elements[1] as InstanceRef;
expect(sixth.valueAsString, '5');
});

test('Lists running off the end', () async {
var list = await createList();
var inst = await service.getObject(isolate.id, list.objectId,
count: 5, offset: 1000) as Instance;
expect(inst.length, 1001);
expect(inst.offset, 1000);
expect(inst.count, 1);
var only = inst.elements[0] as InstanceRef;
expect(only.valueAsString, '5');
});

test('Maps', () async {
var map = await createMap();
var inst =
Expand All @@ -603,33 +557,6 @@ void main() {
expect(sixth.value.valueAsString, '995');
});

test('Maps with count/offset', () async {
var map = await createMap();
var inst = await service.getObject(isolate.id, map.objectId,
count: 7, offset: 4) as Instance;
expect(inst.length, 1001);
expect(inst.offset, 4);
expect(inst.count, 7);
var fifth = inst.associations[0];
expect(fifth.key.valueAsString, '4');
expect(fifth.value.valueAsString, '996');
var sixth = inst.associations[1];
expect(sixth.key.valueAsString, '5');
expect(sixth.value.valueAsString, '995');
});

test('Maps running off the end', () async {
var map = await createMap();
var inst = await service.getObject(isolate.id, map.objectId,
count: 5, offset: 1000) as Instance;
expect(inst.length, 1001);
expect(inst.offset, 1000);
expect(inst.count, 1);
var only = inst.associations[0];
expect(only.key.valueAsString, '1000');
expect(only.value.valueAsString, '0');
});

test('bool', () async {
var ref = await service.evaluate(
isolate.id, bootstrap.id, 'helloBool(true)') as InstanceRef;
Expand Down Expand Up @@ -671,6 +598,190 @@ void main() {
expect(script.tokenPosTable, isNotEmpty);
}
});

group('getObject called with offset/count parameters', () {
test('Lists with offset/count are truncated', () async {
var list = await createList();
var inst = await service.getObject(
isolate.id,
list.objectId,
count: 7,
offset: 4,
) as Instance;
expect(inst.length, 1001);
expect(inst.offset, 4);
expect(inst.count, 7);
var fifth = inst.elements[0] as InstanceRef;
expect(fifth.valueAsString, '100');
var sixth = inst.elements[1] as InstanceRef;
expect(sixth.valueAsString, '5');
});

test('Lists are truncated to the end if offset/count runs off the end',
() async {
var list = await createList();
var inst = await service.getObject(
isolate.id,
list.objectId,
count: 5,
offset: 1000,
) as Instance;
expect(inst.length, 1001);
expect(inst.offset, 1000);
expect(inst.count, 1);
var only = inst.elements[0] as InstanceRef;
expect(only.valueAsString, '5');
});

test('Maps with offset/count are truncated', () async {
var map = await createMap();
var inst = await service.getObject(
isolate.id,
map.objectId,
count: 7,
offset: 4,
) as Instance;
expect(inst.length, 1001);
expect(inst.offset, 4);
expect(inst.count, 7);
var fifth = inst.associations[0];
expect(fifth.key.valueAsString, '4');
expect(fifth.value.valueAsString, '996');
var sixth = inst.associations[1];
expect(sixth.key.valueAsString, '5');
expect(sixth.value.valueAsString, '995');
});

test('Maps are truncated to the end if offset/count runs off the end',
() async {
var map = await createMap();
var inst = await service.getObject(
isolate.id,
map.objectId,
count: 5,
offset: 1000,
) as Instance;
expect(inst.length, 1001);
expect(inst.offset, 1000);
expect(inst.count, 1);
var only = inst.associations[0];
expect(only.key.valueAsString, '1000');
expect(only.value.valueAsString, '0');
});

test('Strings with offset/count are truncated', () async {
var worldRef = await service.evaluate(
isolate.id, bootstrap.id, "helloString('world')") as InstanceRef;
var world = await service.getObject(
isolate.id,
worldRef.id,
count: 2,
offset: 1,
) as Instance;
expect(world.valueAsString, 'or');
expect(world.count, 2);
expect(world.length, 5);
expect(world.offset, 1);
});

test(
'Strings are truncated to the end if offset/count runs off the end',
() async {
var worldRef = await service.evaluate(
isolate.id, bootstrap.id, "helloString('world')") as InstanceRef;
var world = await service.getObject(
isolate.id,
worldRef.id,
count: 5,
offset: 3,
) as Instance;
expect(world.valueAsString, 'ld');
expect(world.count, 2);
expect(world.length, 5);
expect(world.offset, 3);
});

test('offset/count parameters are ignored for Classes', () async {
var testClass = await service.getObject(
isolate.id,
rootLibrary.classes.first.id,
offset: 100,
count: 100,
) as Class;
expect(
testClass.functions,
unorderedEquals([
predicate((FuncRef f) => f.name == 'message' && !f.isStatic),
predicate((FuncRef f) => f.name == 'notFinal' && !f.isStatic),
predicate((FuncRef f) => f.name == 'hello' && !f.isStatic),
predicate((FuncRef f) => f.name == '_equals' && !f.isStatic),
predicate((FuncRef f) => f.name == 'hashCode' && !f.isStatic),
predicate((FuncRef f) => f.name == 'toString' && !f.isStatic),
predicate(
(FuncRef f) => f.name == 'noSuchMethod' && !f.isStatic),
predicate(
(FuncRef f) => f.name == 'runtimeType' && !f.isStatic),
]));
expect(
testClass.fields,
unorderedEquals([
predicate((FieldRef f) =>
f.name == 'message' &&
f.declaredType != null &&
!f.isStatic &&
!f.isConst &&
f.isFinal),
predicate((FieldRef f) =>
f.name == 'notFinal' &&
f.declaredType != null &&
!f.isStatic &&
!f.isConst &&
!f.isFinal),
]));
});

test('offset/count parameters are ignored for bools', () async {
var ref = await service.evaluate(
isolate.id, bootstrap.id, 'helloBool(true)') as InstanceRef;
var obj = await service.getObject(
isolate.id,
ref.id,
offset: 100,
count: 100,
) as Instance;
expect(obj.kind, InstanceKind.kBool);
expect(obj.classRef.name, 'Bool');
expect(obj.valueAsString, 'true');
});

test('offset/count parameters are ignored for nums', () async {
var ref = await service.evaluate(
isolate.id, bootstrap.id, 'helloNum(42)') as InstanceRef;
var obj = await service.getObject(
isolate.id,
ref.id,
offset: 100,
count: 100,
) as Instance;
expect(obj.kind, InstanceKind.kDouble);
expect(obj.classRef.name, 'Double');
expect(obj.valueAsString, '42');
});

test('offset/count parameters are ignored for null', () async {
var ref = await service.evaluate(
isolate.id, bootstrap.id, 'helloNum(null)') as InstanceRef;
var obj = await service.getObject(
isolate.id,
ref.id,
offset: 100,
count: 100,
) as Instance;
expect(obj.kind, InstanceKind.kNull);
expect(obj.classRef.name, 'Null');
expect(obj.valueAsString, 'null');
});
});
});

test('getScripts', () async {
Expand Down