5ee9cf874f
On-heap TypedArrays have empty ArrayBuffers that are not supposed to be accessed directly. Such ArrayBuffers materialize properly when accessed via their TypedArrays. The queryObjects() sidesteps the bottleneck and finds empty ArrayBuffers by iterating the heap. When preview TypedArrays are constructed for the found ArrayBuffers, they get nullptr data pointers. This CL converts all on-heap TypedArrays into off-heap TypedArrays in queryObjects to make sure that all found ArrayBuffers are valid. Bug: chromium:992442 Change-Id: Ie77d1e75aa2007b4a976c72206b9a4e215c9ef53 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2150601 Commit-Queue: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Simon Zünd <szuend@chromium.org> Cr-Commit-Position: refs/heads/master@{#67174}
249 lines
8.4 KiB
JavaScript
249 lines
8.4 KiB
JavaScript
// Copyright 2017 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Flags: --allow-natives-syntax
|
|
|
|
let {session, contextGroup, Protocol} =
|
|
InspectorTest.start('Checks Runtime.queryObjects');
|
|
|
|
InspectorTest.runAsyncTestSuite([
|
|
async function testClass() {
|
|
let contextGroup = new InspectorTest.ContextGroup();
|
|
let session = contextGroup.connect();
|
|
let Protocol = session.Protocol;
|
|
|
|
InspectorTest.log('Declare class Foo & store its constructor.');
|
|
await Protocol.Runtime.evaluate({
|
|
expression: 'class Foo{constructor(){}};'
|
|
});
|
|
let {result:{result:{objectId}}} = await Protocol.Runtime.evaluate({
|
|
expression: 'Foo.prototype'
|
|
});
|
|
|
|
for (let i = 0; i < 2; ++i) {
|
|
InspectorTest.log('Create object with class Foo.');
|
|
Protocol.Runtime.evaluate({expression: 'new Foo()'});
|
|
await queryObjects(session, objectId, 'Foo');
|
|
}
|
|
|
|
session.disconnect();
|
|
},
|
|
|
|
async function testDerivedNewClass() {
|
|
let contextGroup = new InspectorTest.ContextGroup();
|
|
let session = contextGroup.connect();
|
|
let Protocol = session.Protocol;
|
|
|
|
InspectorTest.log('Declare class Foo & store its constructor.');
|
|
Protocol.Runtime.evaluate({expression: 'class Foo{};'});
|
|
let {result:{result:{objectId}}} = await Protocol.Runtime.evaluate({
|
|
expression: 'Foo.prototype'
|
|
});
|
|
let fooConstructorId = objectId;
|
|
|
|
InspectorTest.log('Declare class Boo extends Foo & store its constructor.');
|
|
Protocol.Runtime.evaluate({expression: 'class Boo extends Foo{};'});
|
|
({result:{result:{objectId}}} = await Protocol.Runtime.evaluate({
|
|
expression: 'Boo.prototype'
|
|
}));
|
|
let booConstructorId = objectId;
|
|
|
|
await queryObjects(session, fooConstructorId, 'Foo');
|
|
await queryObjects(session, booConstructorId, 'Boo');
|
|
|
|
InspectorTest.log('Create object with class Foo');
|
|
Protocol.Runtime.evaluate({expression: 'new Foo()'});
|
|
await queryObjects(session, fooConstructorId, 'Foo');
|
|
|
|
InspectorTest.log('Create object with class Boo');
|
|
Protocol.Runtime.evaluate({expression: 'new Boo()'});
|
|
await queryObjects(session, fooConstructorId, 'Foo');
|
|
await queryObjects(session, booConstructorId, 'Boo');
|
|
|
|
session.disconnect();
|
|
},
|
|
|
|
async function testNewFunction() {
|
|
let contextGroup = new InspectorTest.ContextGroup();
|
|
let session = contextGroup.connect();
|
|
let Protocol = session.Protocol;
|
|
|
|
InspectorTest.log('Declare Foo & store it.');
|
|
Protocol.Runtime.evaluate({expression: 'function Foo(){}'});
|
|
let {result:{result:{objectId}}} = await Protocol.Runtime.evaluate({
|
|
expression: 'Foo.prototype'
|
|
});
|
|
|
|
for (let i = 0; i < 2; ++i) {
|
|
InspectorTest.log('Create object using Foo.');
|
|
Protocol.Runtime.evaluate({expression: 'new Foo()'});
|
|
await queryObjects(session, objectId, 'Foo');
|
|
}
|
|
session.disconnect();
|
|
},
|
|
|
|
async function testNonInspectable() {
|
|
let contextGroup = new InspectorTest.ContextGroup();
|
|
let session = contextGroup.connect();
|
|
let Protocol = session.Protocol;
|
|
|
|
InspectorTest.log('Declare Foo & store it.');
|
|
Protocol.Runtime.evaluate({expression: 'function Foo(){}'});
|
|
let {result:{result:{objectId}}} = await Protocol.Runtime.evaluate({
|
|
expression: 'Foo.prototype'
|
|
});
|
|
|
|
InspectorTest.log('Create object using Foo.');
|
|
Protocol.Runtime.evaluate({expression: 'a = new Foo()'});
|
|
await queryObjects(session, objectId, 'Foo');
|
|
InspectorTest.log('Mark object as not inspectable.')
|
|
Protocol.Runtime.evaluate({expression: 'inspector.markObjectAsNotInspectable(a)'});
|
|
await queryObjects(session, objectId, 'Foo');
|
|
session.disconnect();
|
|
},
|
|
|
|
async function testObjectCreate() {
|
|
let contextGroup = new InspectorTest.ContextGroup();
|
|
let session = contextGroup.connect();
|
|
let Protocol = session.Protocol;
|
|
|
|
InspectorTest.log('Declare Object p & store it.');
|
|
let {result:{result:{objectId}}} = await Protocol.Runtime.evaluate({
|
|
expression: 'p = {a:1}'
|
|
});
|
|
for (let i = 0; i < 2; ++i) {
|
|
InspectorTest.log('Create object using Object.create(p).');
|
|
Protocol.Runtime.evaluate({expression: 'Object.create(p)'});
|
|
await queryObjects(session, objectId, 'p');
|
|
}
|
|
session.disconnect();
|
|
},
|
|
|
|
async function testQueryObjectsWithFeedbackVector() {
|
|
let contextGroup = new InspectorTest.ContextGroup();
|
|
let session = contextGroup.connect();
|
|
let Protocol = session.Protocol;
|
|
|
|
let {result:{result:{objectId}}} = await Protocol.Runtime.evaluate({
|
|
expression: 'Object.prototype',
|
|
});
|
|
let countBefore = await countObjects(session, objectId);
|
|
await Protocol.Runtime.evaluate({
|
|
returnByValue: true,
|
|
expression: `
|
|
global.dummyFunction = () => {
|
|
[42];
|
|
{foo: 'bar'};
|
|
[1,2,3];
|
|
}
|
|
%EnsureFeedbackVectorForFunction(dummyFunction);
|
|
dummyFunction();
|
|
`
|
|
});
|
|
let countAfter = await countObjects(session, objectId);
|
|
// Difference should be 1 since |dummyFunction| is retained.
|
|
InspectorTest.log('Before/After difference: ' + (countAfter - countBefore));
|
|
session.disconnect();
|
|
},
|
|
|
|
async function testQueryObjectsWithArrayBuffer() {
|
|
let contextGroup = new InspectorTest.ContextGroup();
|
|
let session = contextGroup.connect();
|
|
let Protocol = session.Protocol;
|
|
|
|
await Protocol.Runtime.evaluate({
|
|
expression: 'new Int8Array(32)',
|
|
});
|
|
let {result:{result:{objectId}}} = await Protocol.Runtime.evaluate({
|
|
expression: 'ArrayBuffer.prototype'
|
|
});
|
|
let {result:{objects}} = await session.Protocol.Runtime.queryObjects({
|
|
prototypeObjectId: objectId
|
|
});
|
|
let {result:{result: properties}} = await session.Protocol.Runtime.getProperties({
|
|
objectId: objects.objectId,
|
|
ownProperties: true,
|
|
generatePreview: true
|
|
});
|
|
await session.Protocol.Runtime.getProperties({
|
|
objectId: properties[0].value.objectId,
|
|
ownProperties: true,
|
|
generatePreview: true
|
|
});
|
|
InspectorTest.log('Test that queryObjects does not crash for on-heap TypedArrays');
|
|
session.disconnect();
|
|
},
|
|
|
|
async function testWithObjectGroup() {
|
|
let contextGroup = new InspectorTest.ContextGroup();
|
|
let session = contextGroup.connect();
|
|
let Protocol = session.Protocol;
|
|
let {result:{result:{objectId}}} = await Protocol.Runtime.evaluate({
|
|
expression: 'Array.prototype'
|
|
});
|
|
|
|
let initialArrayCount;
|
|
const N = 3;
|
|
InspectorTest.log(`Query for Array.prototype ${N} times`);
|
|
for (let i = 0; i < N; ++i) {
|
|
const {result:{objects}} = await Protocol.Runtime.queryObjects({
|
|
prototypeObjectId: objectId,
|
|
objectGroup: 'console'
|
|
});
|
|
logCountSinceInitial(objects.description);
|
|
}
|
|
|
|
await Protocol.Runtime.releaseObjectGroup({objectGroup: 'console'});
|
|
InspectorTest.log('\nReleased object group.');
|
|
|
|
const {result:{objects}} = await Protocol.Runtime.queryObjects({
|
|
prototypeObjectId: objectId,
|
|
objectGroup: 'console'
|
|
});
|
|
logCountSinceInitial(objects.description);
|
|
session.disconnect();
|
|
|
|
function logCountSinceInitial(description) {
|
|
const count = parseInt(/Array\((\d+)\)/.exec(description)[1]);
|
|
if (typeof initialArrayCount === 'undefined')
|
|
initialArrayCount = count;
|
|
InspectorTest.logMessage(`Results since initial: ${count - initialArrayCount}`);
|
|
}
|
|
},
|
|
]);
|
|
|
|
const constructorsNameFunction = `
|
|
function() {
|
|
return this.map(o => o.constructor.name + ',' + typeof o).sort();
|
|
}`;
|
|
|
|
async function queryObjects(sesion, prototypeObjectId, name) {
|
|
let {result:{objects}} = await sesion.Protocol.Runtime.queryObjects({
|
|
prototypeObjectId
|
|
});
|
|
InspectorTest.log(`Query objects with ${name} prototype.`);
|
|
let {result:{result:{value}}} = await sesion.Protocol.Runtime.callFunctionOn({
|
|
objectId: objects.objectId,
|
|
functionDeclaration: constructorsNameFunction,
|
|
returnByValue: true
|
|
});
|
|
InspectorTest.log('Dump each object constructor name.');
|
|
InspectorTest.logMessage(value);
|
|
}
|
|
|
|
async function countObjects(session, prototypeObjectId) {
|
|
let {result:{objects}} = await session.Protocol.Runtime.queryObjects({
|
|
prototypeObjectId
|
|
});
|
|
let {result:{result:{value}}} = await session.Protocol.Runtime.callFunctionOn({
|
|
objectId: objects.objectId,
|
|
functionDeclaration: `function() { return this.length; }`,
|
|
returnByValue: true
|
|
});
|
|
await session.Protocol.Runtime.releaseObject({
|
|
objectId: objects.objectId,
|
|
});
|
|
return value;
|
|
}
|