Commit 86abfd35 authored by Eugene Ostroukhov's avatar Eugene Ostroukhov Committed by Commit Bot

[inspector] provide ArrayBuffer previews

Add an easy way to preview ArrayBuffer as a typed array. This change
will always allow previewing ArrayBuffer instances as Uint8Array and
Int8Array. ArrayBuffer instances that have even length will allow
Int16Array preview and ArrayBuffers that have length divisible by 4 will
allow Int32Array previews.

Bug: 
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I07440147cc9e83c8a987f9316bd8d1b936db2717
Reviewed-on: https://chromium-review.googlesource.com/842472
Commit-Queue: Eugene Ostroukhov <eostroukhov@chromium.org>
Reviewed-by: 's avatarAleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50315}
parent ce609dba
......@@ -460,6 +460,10 @@ InjectedScript.prototype = {
if (InjectedScriptHost.subtype(o) === "proxy")
continue;
var typedArrays = subtype === "arraybuffer" ? InjectedScriptHost.typedArrayProperties(o) || [] : [];
for (var i = 0; i < typedArrays.length; i += 2)
addPropertyIfNeeded(descriptors, { name: typedArrays[i], value: typedArrays[i + 1], isOwn: true, enumerable: false, configurable: false, __proto__: null });
try {
if (skipGetOwnPropertyNames && o === object) {
if (!process(o, undefined, o.length))
......@@ -934,6 +938,10 @@ InjectedScript.RemoteObject.prototype = {
if ((subtype === "map" || subtype === "set") && descriptor.name === "size")
return true;
// Ignore ArrayBuffer previews
if (subtype === 'arraybuffer' && (descriptor.name === "[[Int8Array]]" || descriptor.name === "[[Uint8Array]]" || descriptor.name === "[[Int16Array]]" || descriptor.name === "[[Int32Array]]"))
return true;
// Never preview prototype properties.
if (!descriptor.isOwn)
return true;
......
......@@ -108,6 +108,12 @@ InjectedScriptHostClass.prototype.getOwnPropertySymbols = function(obj) {}
*/
InjectedScriptHostClass.prototype.nativeAccessorDescriptor = function(obj, name) {}
/**
* @param {!Object} arrayBuffer
* @return {Array<Object>|undefined}
*/
InjectedScriptHostClass.prototype.typedArrayProperties = function(arrayBuffer) {}
/** @type {!InjectedScriptHostClass} */
var InjectedScriptHost;
/** @type {!Window} */
......
......@@ -44,6 +44,15 @@ V8InspectorImpl* unwrapInspector(
return inspector;
}
template <typename TypedArray>
void addTypedArrayProperty(std::vector<v8::Local<v8::Value>>* props,
v8::Isolate* isolate,
v8::Local<v8::ArrayBuffer> arraybuffer,
String16 name, size_t length) {
props->push_back(toV8String(isolate, name));
props->push_back(TypedArray::New(arraybuffer, 0, length));
}
} // namespace
v8::Local<v8::Object> V8InjectedScriptHost::create(
......@@ -84,6 +93,9 @@ v8::Local<v8::Object> V8InjectedScriptHost::create(
setFunctionProperty(context, injectedScriptHost, "nativeAccessorDescriptor",
V8InjectedScriptHost::nativeAccessorDescriptorCallback,
debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "typedArrayProperties",
V8InjectedScriptHost::typedArrayPropertiesCallback,
debuggerExternal);
createDataProperty(context, injectedScriptHost,
toV8StringInternalized(isolate, "keys"),
v8::debug::GetBuiltin(isolate, v8::debug::kObjectKeys));
......@@ -374,4 +386,40 @@ void V8InjectedScriptHost::nativeAccessorDescriptorCallback(
info.GetReturnValue().Set(result);
}
void V8InjectedScriptHost::typedArrayPropertiesCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
if (info.Length() != 1 || !info[0]->IsArrayBuffer()) return;
v8::TryCatch tryCatch(isolate);
v8::Isolate::DisallowJavascriptExecutionScope throwJs(
isolate, v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
v8::Local<v8::ArrayBuffer> arrayBuffer = info[0].As<v8::ArrayBuffer>();
size_t length = arrayBuffer->ByteLength();
if (length == 0) return;
std::vector<v8::Local<v8::Value>> arrays_vector;
addTypedArrayProperty<v8::Int8Array>(&arrays_vector, isolate, arrayBuffer,
"[[Int8Array]]", length);
addTypedArrayProperty<v8::Uint8Array>(&arrays_vector, isolate, arrayBuffer,
"[[Uint8Array]]", length);
if (length % 2 == 0) {
addTypedArrayProperty<v8::Int16Array>(&arrays_vector, isolate, arrayBuffer,
"[[Int16Array]]", length / 2);
}
if (length % 4 == 0) {
addTypedArrayProperty<v8::Int32Array>(&arrays_vector, isolate, arrayBuffer,
"[[Int32Array]]", length / 4);
}
if (tryCatch.HasCaught()) return;
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> arrays =
v8::Array::New(isolate, static_cast<uint32_t>(arrays_vector.size()));
for (uint32_t i = 0; i < static_cast<uint32_t>(arrays_vector.size()); i++)
createDataProperty(context, arrays, i, arrays_vector[i]);
if (tryCatch.HasCaught()) return;
info.GetReturnValue().Set(arrays);
}
} // namespace v8_inspector
......@@ -44,6 +44,8 @@ class V8InjectedScriptHost {
const v8::FunctionCallbackInfo<v8::Value>&);
static void nativeAccessorDescriptorCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void typedArrayPropertiesCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
};
} // namespace v8_inspector
......
......@@ -50,3 +50,40 @@ Running test: testObjectThrowsLength
Running test: testTypedArrayWithoutLength
__proto__ own object undefined
Running test: testArrayBuffer
[[Int8Array]]
0 own number 1
1 own number 2
2 own number 3
3 own number 4
4 own number 5
5 own number 6
6 own number 7
7 own number 8
__proto__ own object undefined
[[Uint8Array]]
0 own number 1
1 own number 2
2 own number 3
3 own number 4
4 own number 5
5 own number 6
6 own number 7
7 own number 8
__proto__ own object undefined
[[Int16Array]]
0 own number 513
1 own number 1027
2 own number 1541
3 own number 2055
__proto__ own object undefined
[[Int32Array]]
0 own number 67305985
1 own number 134678021
__proto__ own object undefined
Running test: testArrayBufferWithBrokenUintCtor
[[Int8Array]] own object undefined
[[Uint8Array]] own object undefined
__proto__ own object undefined
......@@ -5,71 +5,77 @@
let {session, contextGroup, Protocol} = InspectorTest.start('Checks Runtime.getProperties method');
InspectorTest.runAsyncTestSuite([
async function testObject5() {
let objectId = (await Protocol.Runtime.evaluate({
expression: '(function(){var r = Object(5); r.foo = \'cat\';return r;})()'
})).result.result.objectId;
let props = await Protocol.Runtime.getProperties({ objectId, ownProperties: true });
logGetPropertiesResult(props.result);
function testObject5() {
return logExpressionProperties('(function(){var r = Object(5); r.foo = \'cat\';return r;})()');
},
async function testNotOwn() {
let objectId = (await Protocol.Runtime.evaluate({
expression: '({ a: 2, set b(_) {}, get b() {return 5;}, __proto__: { a: 3, c: 4, get d() {return 6;} }})'
})).result.result.objectId;
let props = await Protocol.Runtime.getProperties({ objectId, ownProperties: false });
logGetPropertiesResult(props.result);
function testNotOwn() {
return logExpressionProperties('({ a: 2, set b(_) {}, get b() {return 5;}, __proto__: { a: 3, c: 4, get d() {return 6;} }})', { ownProperties: false });
},
async function testAccessorsOnly() {
let objectId = (await Protocol.Runtime.evaluate({
expression: '({ a: 2, set b(_) {}, get b() {return 5;}, c: \'c\', set d(_){} })'
})).result.result.objectId;
let props = await Protocol.Runtime.getProperties({ objectId, ownProperties: true, accessorPropertiesOnly: true });
logGetPropertiesResult(props.result);
function testAccessorsOnly() {
return logExpressionProperties('({ a: 2, set b(_) {}, get b() {return 5;}, c: \'c\', set d(_){} })', { ownProperties: true, accessorPropertiesOnly: true});
},
async function testArray() {
let objectId = (await Protocol.Runtime.evaluate({
expression: '[\'red\', \'green\', \'blue\']'
})).result.result.objectId;
let props = await Protocol.Runtime.getProperties({ objectId, ownProperties: true });
logGetPropertiesResult(props.result);
function testArray() {
return logExpressionProperties('[\'red\', \'green\', \'blue\']');
},
async function testBound() {
let objectId = (await Protocol.Runtime.evaluate({
expression: 'Number.bind({}, 5)'
})).result.result.objectId;
let props = await Protocol.Runtime.getProperties({ objectId, ownProperties: true });
logGetPropertiesResult(props.result);
function testBound() {
return logExpressionProperties('Number.bind({}, 5)');
},
async function testObjectThrowsLength() {
let objectId = (await Protocol.Runtime.evaluate({
expression: '({get length() { throw \'Length called\'; }})'
})).result.result.objectId;
let props = await Protocol.Runtime.getProperties({ objectId, ownProperties: true });
logGetPropertiesResult(props.result);
function testObjectThrowsLength() {
return logExpressionProperties('({get length() { throw \'Length called\'; }})');
},
async function testTypedArrayWithoutLength() {
let objectId = (await Protocol.Runtime.evaluate({
expression: '({__proto__: Uint8Array.prototype})'
})).result.result.objectId;
function testTypedArrayWithoutLength() {
return logExpressionProperties('({__proto__: Uint8Array.prototype})');
},
async function testArrayBuffer() {
let objectId = await evaluateToObjectId('new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]).buffer');
let props = await Protocol.Runtime.getProperties({ objectId, ownProperties: true });
logGetPropertiesResult(props.result);
for (let prop of props.result.result) {
if (prop.name === '__proto__')
continue;
InspectorTest.log(prop.name);
await logGetPropertiesResult(prop.value.objectId);
}
},
async function testArrayBufferWithBrokenUintCtor() {
await evaluateToObjectId(`(function() {
this.uint8array_old = this.Uint8Array;
this.Uint8Array = 42;
})()`);
await logExpressionProperties('new Int8Array([1, 2, 3, 4, 5, 6, 7]).buffer');
await evaluateToObjectId(`(function() {
this.Uint8Array = this.uint8array_old;
delete this.uint8array_old;
})()`);
}
]);
function logGetPropertiesResult(protocolResult) {
async function logExpressionProperties(expression, flags) {
const objectId = await evaluateToObjectId(expression);
return await logGetPropertiesResult(objectId, flags);
}
async function evaluateToObjectId(expression) {
return (await Protocol.Runtime.evaluate({ expression })).result.result.objectId;
}
async function logGetPropertiesResult(objectId, flags = { ownProperties: true }) {
function hasGetterSetter(property, fieldName) {
var v = property[fieldName];
if (!v) return false;
return v.type !== "undefined"
}
var propertyArray = protocolResult.result;
flags.objectId = objectId;
let props = await Protocol.Runtime.getProperties(flags);
var propertyArray = props.result.result;
propertyArray.sort(NamedThingComparator);
for (var i = 0; i < propertyArray.length; i++) {
var p = propertyArray[i];
......@@ -81,7 +87,7 @@ function logGetPropertiesResult(protocolResult) {
InspectorTest.log(" " + p.name + " " + own + " no value" +
(hasGetterSetter(p, "get") ? ", getter" : "") + (hasGetterSetter(p, "set") ? ", setter" : ""));
}
var internalPropertyArray = protocolResult.internalProperties;
var internalPropertyArray = props.result.internalProperties;
if (internalPropertyArray) {
InspectorTest.log("Internal properties");
internalPropertyArray.sort(NamedThingComparator);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment