зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
6293acae53
Коммит
d2046126d5
|
@ -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]
|
||||
|
|
Загрузка…
Ссылка в новой задаче