Bug 1165807 - display WeakSet and WeakMap contents in console; r=bz,fitzgen

--HG--
extra : commitid : 21J7fnJqwm5
extra : rebase_source : 95bf00beb87284b27c22c7497aa73ded0f05bfe2
This commit is contained in:
Tom Tromey 2015-11-16 06:50:00 -08:00
Родитель 6293acae53
Коммит d2046126d5
13 изменённых файлов: 253 добавлений и 7 удалений

Просмотреть файл

@ -1820,7 +1820,8 @@ Messages.ConsoleTable.prototype = Heritage.extend(Messages.Extended.prototype,
let data = this._arguments[0];
if (data.class != "Array" && data.class != "Object" &&
data.class != "Map" && data.class != "Set") {
data.class != "Map" && data.class != "Set" &&
data.class != "WeakMap" && data.class != "WeakSet") {
return;
}
@ -1900,7 +1901,7 @@ Messages.ConsoleTable.prototype = Heritage.extend(Messages.Extended.prototype,
deferred.resolve();
});
} else if (data.class == "Map") {
} else if (data.class == "Map" || data.class == "WeakMap") {
let entries = data.preview.entries;
if (!hasColumnsArg) {
@ -1925,7 +1926,7 @@ Messages.ConsoleTable.prototype = Heritage.extend(Messages.Extended.prototype,
}
deferred.resolve();
} else if (data.class == "Set") {
} else if (data.class == "Set" || data.class == "WeakSet") {
let entries = data.preview.items;
if (!hasColumnsArg) {

Просмотреть файл

@ -153,6 +153,26 @@ var inputTests = [
inspectable: true,
variablesViewLabel: "Map[3]",
},
// 15 - WeakSet
{
input: "window.weakset",
// Need a regexp because the order may vary.
output: new RegExp("WeakSet \\[ (String\\[7\\], <head>|<head>, String\\[7\\]) \\]"),
printOutput: "[object WeakSet]",
inspectable: true,
variablesViewLabel: "WeakSet[2]",
},
// 16 - WeakMap
{
input: "window.weakmap",
// Need a regexp because the order may vary.
output: new RegExp("WeakMap { (String\\[7\\]: 23, HTMLCollection\\[2\\]: Object|HTMLCollection\\[2\\]: Object, String\\[7\\]: 23) }"),
printOutput: "[object WeakMap]",
inspectable: true,
variablesViewLabel: "WeakMap[2]",
},
];
function test() {

Просмотреть файл

@ -120,7 +120,25 @@ const TEST_DATA = [
{ _index: 1, _key: "5", _value: "\"value associated with 5\"" },
],
columns: { _index: "(iteration index)", _key: "Key", _value: "Values" }
}
},
{
command: "console.table(weakset)",
data: [
{ _value: "String[7]" },
{ _value: "String[7]" },
],
columns: { _index: "(iteration index)", _value: "Values" },
couldBeOutOfOrder: true,
},
{
command: "console.table(weakmap)",
data: [
{ _key: "String[7]", _value: "\"oh no\"" },
{ _key: "String[7]", _value: "23" },
],
columns: { _index: "(iteration index)", _key: "Key", _value: "Values" },
couldBeOutOfOrder: true,
},
];
add_task(function*() {
@ -156,14 +174,23 @@ add_task(function*() {
let entryResult = {};
for (let key of Object.keys(entries)) {
entryResult[key] = entries[key] instanceof HTMLElement ?
entries[key].textContent : entries[key];
// If the results can be out of order, then ignore _index.
if (!testdata.couldBeOutOfOrder || key !== "_index") {
entryResult[key] = entries[key] instanceof HTMLElement ?
entries[key].textContent : entries[key];
}
}
return entryResult;
});
is(data.toSource(), testdata.data.toSource(), "table data is correct");
if (testdata.couldBeOutOfOrder) {
data = data.map(e => e.toSource()).sort().join(",");
let expected = testdata.data.map(e => e.toSource()).sort().join(",");
is(data, expected, "table data is correct");
} else {
is(data.toSource(), testdata.data.toSource(), "table data is correct");
}
ok(obj._columns, "found table column object");
is(obj._columns.toSource(), testdata.columns.toSource(),
"table column is correct");

Просмотреть файл

@ -36,6 +36,9 @@ var typedarray1 = new Int32Array([1, 287, 8651, 40983, 8754]);
var set1 = new Set([1, 2, null, array3, "a", "b", undefined, document.head]);
set1.add(set1);
var bunnies = new String("bunnies")
var weakset = new WeakSet([bunnies, document.head]);
var testobj2 = {a: "b", c: "d", e: 1, f: "2"};
testobj2.foo = testobj1;
testobj2.bar = testobj2;
@ -56,6 +59,8 @@ Object.defineProperty(testobj4, "nonEnumerable", { value: "hello world" });
var map1 = new Map([["a", "b"], [document.body.children, testobj2]]);
map1.set(map1, set1);
var weakmap = new WeakMap([[bunnies, 23], [document.body.children, testobj2]]);
</script>
</body>
</html>

Просмотреть файл

@ -44,6 +44,17 @@
mySet.add("some text");
mySet.add(null);
mySet.add(undefined);
// These are globals and so won't be reclaimed by the GC.
var bunnies = new String("bunnies");
var lizards = new String("lizards");
var weakmap = new WeakMap();
weakmap.set(bunnies, 23);
weakmap.set(lizards, "oh no");
var weakset = new WeakSet([bunnies, lizards]);
</script>
</head>
<body>

Просмотреть файл

@ -13,6 +13,8 @@ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { assert, dumpn } = DevToolsUtils;
const PromiseDebugging = require("PromiseDebugging");
loader.lazyRequireGetter(this, "ThreadSafeChromeUtils");
const TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
"Uint32Array", "Int8Array", "Int16Array", "Int32Array", "Float32Array",
"Float64Array"];
@ -1067,6 +1069,43 @@ DebuggerServer.ObjectActorPreviewers = {
return true;
}],
WeakSet: [function({obj, hooks}, grip) {
let raw = obj.unsafeDereference();
// We currently lack XrayWrappers for WeakSet, so when we iterate over
// the values, the temporary iterator objects get created in the target
// compartment. However, we _do_ have Xrays to Object now, so we end up
// Xraying those temporary objects, and filtering access to |it.value|
// based on whether or not it's Xrayable and/or callable, which breaks
// the for/of iteration.
//
// This code is designed to handle untrusted objects, so we can safely
// waive Xrays on the iterable, and relying on the Debugger machinery to
// make sure we handle the resulting objects carefully.
let keys = Cu.waiveXrays(ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(raw));
grip.preview = {
kind: "ArrayLike",
length: keys.length,
};
// Avoid recursive object grips.
if (hooks.getGripDepth() > 1) {
return true;
}
let items = grip.preview.items = [];
for (let item of keys) {
item = Cu.unwaiveXrays(item);
item = makeDebuggeeValueIfNeeded(obj, item);
items.push(hooks.createValueGrip(item));
if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
break;
}
}
return true;
}],
Map: [function({obj, hooks}, grip) {
let size = DevToolsUtils.getProperty(obj, "size");
if (typeof size != "number") {
@ -1111,6 +1150,45 @@ DebuggerServer.ObjectActorPreviewers = {
return true;
}],
WeakMap: [function({obj, hooks}, grip) {
let raw = obj.unsafeDereference();
// We currently lack XrayWrappers for WeakMap, so when we iterate over
// the values, the temporary iterator objects get created in the target
// compartment. However, we _do_ have Xrays to Object now, so we end up
// Xraying those temporary objects, and filtering access to |it.value|
// based on whether or not it's Xrayable and/or callable, which breaks
// the for/of iteration.
//
// This code is designed to handle untrusted objects, so we can safely
// waive Xrays on the iterable, and relying on the Debugger machinery to
// make sure we handle the resulting objects carefully.
let rawEntries = Cu.waiveXrays(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(raw));
grip.preview = {
kind: "MapLike",
size: rawEntries.length,
};
if (hooks.getGripDepth() > 1) {
return true;
}
let entries = grip.preview.entries = [];
for (let key of rawEntries) {
let value = Cu.unwaiveXrays(WeakMap.prototype.get.call(raw, key));
key = Cu.unwaiveXrays(key);
key = makeDebuggeeValueIfNeeded(obj, key);
value = makeDebuggeeValueIfNeeded(obj, value);
entries.push([hooks.createValueGrip(key),
hooks.createValueGrip(value)]);
if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
break;
}
}
return true;
}],
DOMStringMap: [function({obj, hooks}, grip, rawObj) {
if (!rawObj) {
return false;

Просмотреть файл

@ -30,6 +30,26 @@ ThreadSafeChromeUtils::NondeterministicGetWeakMapKeys(GlobalObject& aGlobal,
}
}
/* static */ void
ThreadSafeChromeUtils::NondeterministicGetWeakSetKeys(GlobalObject& aGlobal,
JS::Handle<JS::Value> aSet,
JS::MutableHandle<JS::Value> aRetval,
ErrorResult& aRv)
{
if (!aSet.isObject()) {
aRetval.setUndefined();
} else {
JSContext* cx = aGlobal.Context();
JS::Rooted<JSObject*> objRet(cx);
JS::Rooted<JSObject*> setObj(cx, &aSet.toObject());
if (!JS_NondeterministicGetWeakSetKeys(cx, setObj, &objRet)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
} else {
aRetval.set(objRet ? JS::ObjectValue(*objRet) : JS::UndefinedValue());
}
}
}
/* static */ void
ChromeUtils::OriginAttributesToSuffix(dom::GlobalObject& aGlobal,
const dom::OriginAttributesDictionary& aAttrs,

Просмотреть файл

@ -40,6 +40,11 @@ public:
JS::Handle<JS::Value> aMap,
JS::MutableHandle<JS::Value> aRetval,
ErrorResult& aRv);
static void NondeterministicGetWeakSetKeys(GlobalObject& aGlobal,
JS::Handle<JS::Value> aSet,
JS::MutableHandle<JS::Value> aRetval,
ErrorResult& aRv);
};
class ChromeUtils : public ThreadSafeChromeUtils

Просмотреть файл

@ -43,6 +43,18 @@ interface ThreadSafeChromeUtils {
*/
[Throws, NewObject]
static any nondeterministicGetWeakMapKeys(any map);
/**
* Return the keys in a weak set. This operation is
* non-deterministic because it is affected by the scheduling of the
* garbage collector and the cycle collector.
*
* @param aSet weak set or other JavaScript value
* @returns If aSet is a weak set object, return the keys of the weak
* set as an array. Otherwise, return undefined.
*/
[Throws, NewObject]
static any nondeterministicGetWeakSetKeys(any aSet);
};
/**

Просмотреть файл

@ -156,3 +156,21 @@ js::InitWeakSetClass(JSContext* cx, HandleObject obj)
{
return WeakSetObject::initClass(cx, obj);
}
JS_FRIEND_API(bool)
JS_NondeterministicGetWeakSetKeys(JSContext* cx, HandleObject objArg, MutableHandleObject ret)
{
RootedObject obj(cx, objArg);
obj = UncheckedUnwrap(obj);
if (!obj || !obj->is<WeakSetObject>()) {
ret.set(nullptr);
return true;
}
Rooted<WeakSetObject*> weakset(cx, &obj->as<WeakSetObject>());
if (!weakset)
return false;
RootedObject map(cx, &weakset->getReservedSlot(WEAKSET_MAP_SLOT).toObject());
return JS_NondeterministicGetWeakMapKeys(cx, map, ret);
}

Просмотреть файл

@ -81,6 +81,9 @@ JS_GetCustomIteratorCount(JSContext* cx);
extern JS_FRIEND_API(bool)
JS_NondeterministicGetWeakMapKeys(JSContext* cx, JS::HandleObject obj, JS::MutableHandleObject ret);
extern JS_FRIEND_API(bool)
JS_NondeterministicGetWeakSetKeys(JSContext* cx, JS::HandleObject obj, JS::MutableHandleObject ret);
// Raw JSScript* because this needs to be callable from a signal handler.
extern JS_FRIEND_API(unsigned)
JS_PCToLineNumber(JSScript* script, jsbytecode* pc, unsigned* columnp = nullptr);

Просмотреть файл

@ -0,0 +1,45 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* See https://bugzilla.mozilla.org/show_bug.cgi?id=1165807 */
function run_test()
{
var bunnies = new String("bunnies");
var lizards = new String("lizards");
var weakset = new WeakSet([bunnies, lizards]);
var weakmap = new WeakMap();
weakmap.set(bunnies, 23);
weakmap.set(lizards, "oh no");
var keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(bunnies);
equal(keys, undefined, "test nondeterministicGetWeakMapKeys on non-WeakMap");
keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(weakmap);
equal(keys.length, 2, "length of nondeterministicGetWeakMapKeys");
equal(weakmap.get(bunnies), 23, "check bunnies in weakmap");
equal(weakmap.get(lizards), "oh no", "check lizards in weakmap");
keys = ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(bunnies);
equal(keys, undefined, "test nondeterministicGetWeakSetKeys on non-WeakMap");
keys = ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(weakset);
equal(keys.length, 2, "length of nondeterministicGetWeakSetKeys");
ok(weakset.has(bunnies), "check bunnies in weakset");
ok(weakset.has(lizards), "check lizards in weakset");
bunnies = null;
keys = null;
Components.utils.forceGC();
keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(weakmap);
equal(keys.length, 1, "length of nondeterministicGetWeakMapKeys after GC");
equal(weakmap.get(lizards), "oh no", "check lizards still in weakmap");
keys = ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(weakset);
equal(keys.length, 1, "length of nondeterministicGetWeakSetKeys after GC");
ok(weakset.has(lizards), "check lizards still in weakset");
}

Просмотреть файл

@ -126,6 +126,7 @@ head = head_watchdog.js
head = head_watchdog.js
[test_watchdog_hibernate.js]
head = head_watchdog.js
[test_weak_keys.js]
[test_writeToGlobalPrototype.js]
[test_xpcwn_tamperproof.js]
[test_xrayed_iterator.js]