diff --git a/src/inspector/v8-injected-script-host.cc b/src/inspector/v8-injected-script-host.cc index dc41ef8631..3748ec9aa3 100644 --- a/src/inspector/v8-injected-script-host.cc +++ b/src/inspector/v8-injected-script-host.cc @@ -166,12 +166,69 @@ void V8InjectedScriptHost::subtypeCallback( void V8InjectedScriptHost::getInternalPropertiesCallback( const v8::FunctionCallbackInfo& info) { if (info.Length() < 1) return; - v8::Local properties; - if (unwrapInspector(info) - ->debugger() - ->internalProperties(info.GetIsolate()->GetCurrentContext(), info[0]) - .ToLocal(&properties)) + + std::unordered_set allowedProperties; + if (info[0]->IsBooleanObject() || info[0]->IsNumberObject() || + info[0]->IsStringObject() || info[0]->IsSymbolObject()) { + allowedProperties.insert(String16("[[PrimitiveValue]]")); + } else if (info[0]->IsPromise()) { + allowedProperties.insert(String16("[[PromiseStatus]]")); + allowedProperties.insert(String16("[[PromiseValue]]")); + } else if (info[0]->IsGeneratorObject()) { + allowedProperties.insert(String16("[[GeneratorStatus]]")); + } else if (info[0]->IsMapIterator() || info[0]->IsSetIterator()) { + allowedProperties.insert(String16("[[IteratorHasMore]]")); + allowedProperties.insert(String16("[[IteratorIndex]]")); + allowedProperties.insert(String16("[[IteratorKind]]")); + allowedProperties.insert(String16("[[Entries]]")); + } else if (info[0]->IsMap() || info[0]->IsWeakMap() || info[0]->IsSet() || + info[0]->IsWeakSet()) { + allowedProperties.insert(String16("[[Entries]]")); + } + if (!allowedProperties.size()) return; + + v8::Isolate* isolate = info.GetIsolate(); + v8::Local allProperties; + if (!unwrapInspector(info) + ->debugger() + ->internalProperties(isolate->GetCurrentContext(), info[0]) + .ToLocal(&allProperties) || + !allProperties->IsArray() || allProperties->Length() % 2 != 0) + return; + + { + v8::Local context = isolate->GetCurrentContext(); + v8::TryCatch tryCatch(isolate); + v8::Isolate::DisallowJavascriptExecutionScope throwJs( + isolate, + v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); + + v8::Local properties = v8::Array::New(isolate); + if (tryCatch.HasCaught()) return; + + uint32_t outputIndex = 0; + for (uint32_t i = 0; i < allProperties->Length(); i += 2) { + v8::Local key; + if (!allProperties->Get(context, i).ToLocal(&key)) continue; + if (tryCatch.HasCaught()) { + tryCatch.Reset(); + continue; + } + String16 keyString = toProtocolStringWithTypeCheck(key); + if (keyString.isEmpty() || + allowedProperties.find(keyString) == allowedProperties.end()) + continue; + v8::Local value; + if (!allProperties->Get(context, i + 1).ToLocal(&value)) continue; + if (tryCatch.HasCaught()) { + tryCatch.Reset(); + continue; + } + createDataProperty(context, properties, outputIndex++, key); + createDataProperty(context, properties, outputIndex++, value); + } info.GetReturnValue().Set(properties); + } } void V8InjectedScriptHost::objectHasOwnPropertyCallback( diff --git a/test/inspector/debugger/object-preview-internal-properties-expected.txt b/test/inspector/debugger/object-preview-internal-properties-expected.txt new file mode 100644 index 0000000000..2c7995c335 --- /dev/null +++ b/test/inspector/debugger/object-preview-internal-properties-expected.txt @@ -0,0 +1,253 @@ +Check internal properties reported in object preview. + +Running test: boxedObjects +expression: new Number(239) +{ + name : [[PrimitiveValue]] + type : number + value : 239 +} + +expression: new Boolean(false) +{ + name : [[PrimitiveValue]] + type : boolean + value : false +} + +expression: new String("abc") +{ + name : [[PrimitiveValue]] + type : string + value : abc +} + +expression: Object(Symbol(42)) +{ + name : [[PrimitiveValue]] + type : symbol + valuePreview : { + description : Symbol + overflow : false + properties : [ + ] + type : object + } +} + + +Running test: promise +expression: Promise.resolve(42) +{ + name : [[PromiseStatus]] + type : string + value : resolved +} +{ + name : [[PromiseValue]] + type : number + value : 42 +} + +expression: new Promise(() => undefined) +{ + name : [[PromiseStatus]] + type : string + value : pending +} +{ + name : [[PromiseValue]] + type : undefined + value : undefined +} + + +Running test: generatorObject +expression: (function* foo() { yield 1 })() +{ + name : [[GeneratorStatus]] + type : string + value : suspended +} + + +Running test: entriesInMapAndSet +expression: new Map([[1,2]]) +[[Entries]]: +[ + [0] : { + key : { + description : 1 + overflow : false + properties : [ + ] + type : number + } + value : { + description : 2 + overflow : false + properties : [ + ] + type : number + } + } +] + +expression: new Set([1]) +[[Entries]]: +[ + [0] : { + value : { + description : 1 + overflow : false + properties : [ + ] + type : number + } + } +] + +expression: new WeakMap([[{}, 42]]) +[[Entries]]: +[ + [0] : { + key : { + description : Object + overflow : false + properties : [ + ] + type : object + } + value : { + description : 42 + overflow : false + properties : [ + ] + type : number + } + } +] + +expression: new WeakSet([{}]) +[[Entries]]: +[ + [0] : { + value : { + description : Object + overflow : false + properties : [ + ] + type : object + } + } +] + + +Running test: iteratorObject +expression: (new Map([[1,2]])).entries() +{ + name : [[IteratorHasMore]] + type : boolean + value : true +} +{ + name : [[IteratorIndex]] + type : number + value : 0 +} +{ + name : [[IteratorKind]] + type : string + value : entries +} +[[Entries]]: +[ + [0] : { + value : { + description : Array[2] + overflow : false + properties : [ + [0] : { + name : 0 + type : number + value : 1 + } + [1] : { + name : 1 + type : number + value : 2 + } + ] + subtype : array + type : object + } + } +] + +expression: (new Set([[1,2]])).entries() +{ + name : [[IteratorHasMore]] + type : boolean + value : true +} +{ + name : [[IteratorIndex]] + type : number + value : 0 +} +{ + name : [[IteratorKind]] + type : string + value : entries +} +[[Entries]]: +[ + [0] : { + value : { + description : Array[2] + overflow : false + properties : [ + [0] : { + name : 0 + subtype : array + type : object + value : Array[2] + } + [1] : { + name : 1 + subtype : array + type : object + value : Array[2] + } + ] + subtype : array + type : object + } + } +] + + +Running test: noPreviewForFunctionObject +(function foo(){}) +{ + id : + result : { + result : { + className : Function + description : function foo(){} + objectId : + type : function + } + } +} + +Running test: otherObjects +expression: [1,2,3] + +expression: /123/ + +expression: ({}) + + +Running test: overridenArrayGetter +expression: Promise.resolve(42) diff --git a/test/inspector/debugger/object-preview-internal-properties.js b/test/inspector/debugger/object-preview-internal-properties.js new file mode 100644 index 0000000000..00b1d5bf45 --- /dev/null +++ b/test/inspector/debugger/object-preview-internal-properties.js @@ -0,0 +1,107 @@ +// Copyright 2016 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. + +print("Check internal properties reported in object preview."); + +Protocol.Debugger.enable(); +Protocol.Runtime.enable(); +Protocol.Runtime.onConsoleAPICalled(dumpInternalPropertiesAndEntries); + +InspectorTest.runTestSuite([ + function boxedObjects(next) + { + checkExpression("new Number(239)") + .then(() => checkExpression("new Boolean(false)")) + .then(() => checkExpression("new String(\"abc\")")) + .then(() => checkExpression("Object(Symbol(42))")) + .then(next); + }, + + function promise(next) + { + checkExpression("Promise.resolve(42)") + .then(() => checkExpression("new Promise(() => undefined)")) + .then(next); + }, + + function generatorObject(next) + { + checkExpression("(function* foo() { yield 1 })()") + .then(next); + }, + + function entriesInMapAndSet(next) + { + checkExpression("new Map([[1,2]])") + .then(() => checkExpression("new Set([1])")) + .then(() => checkExpression("new WeakMap([[{}, 42]])")) + .then(() => checkExpression("new WeakSet([{}])")) + .then(next); + }, + + function iteratorObject(next) + { + checkExpression("(new Map([[1,2]])).entries()") + .then(() => checkExpression("(new Set([[1,2]])).entries()")) + .then(next); + }, + + function noPreviewForFunctionObject(next) + { + var expression = "(function foo(){})"; + InspectorTest.log(expression); + Protocol.Runtime.evaluate({ expression: expression, generatePreview: true}) + .then(message => InspectorTest.logMessage(message)) + .then(next); + }, + + function otherObjects(next) + { + checkExpression("[1,2,3]") + .then(() => checkExpression("/123/")) + .then(() => checkExpression("({})")) + .then(next); + }, + + function overridenArrayGetter(next) + { + Protocol.Runtime.evaluate({ expression: "Array.prototype.__defineGetter__(\"0\",() => { throw new Error() }) "}) + .then(() => checkExpression("Promise.resolve(42)")) + .then(next); + } +]); + +function checkExpression(expression) +{ + InspectorTest.log(`expression: ${expression}`); + // console.table has higher limits for internal properties amount in preview. + return Protocol.Runtime.evaluate({ expression: `console.table(${expression})`, generatePreview: true }); +} + +function dumpInternalPropertiesAndEntries(message) +{ + var properties; + var entries; + try { + var preview = message.params.args[0].preview; + properties = preview.properties; + entries = preview.entries; + } catch (e) { + InspectorTest.logMessage(message); + return; + } + if (!properties) { + InspectorTest.logMessage(message); + return; + } + for (var property of properties) { + if (property.name.startsWith("[[")) + InspectorTest.logMessage(property); + } + if (entries) { + InspectorTest.log("[[Entries]]:"); + InspectorTest.logMessage(entries); + } + InspectorTest.log(""); +}