mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 22:55:23 +00:00
Bug 805003 - Implement Map and Set clear methods. r=luke.
--HG-- extra : rebase_source : 02c641c57f556f83dbaf6fbf568f0d67e53c9982
This commit is contained in:
parent
b880ee6c47
commit
15a89dedfc
@ -111,6 +111,8 @@ class OrderedHashTable
|
||||
return false;
|
||||
}
|
||||
|
||||
// clear() requires that members are assigned only after all allocation
|
||||
// has succeeded, and that this->ranges is left untouched.
|
||||
hashTable = tableAlloc;
|
||||
data = dataAlloc;
|
||||
dataLength = 0;
|
||||
@ -218,6 +220,42 @@ class OrderedHashTable
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all entries.
|
||||
*
|
||||
* Returns false on OOM, leaving the OrderedHashTable and any live Ranges
|
||||
* in the old state.
|
||||
*
|
||||
* The effect on live Ranges is the same as removing all entries; in
|
||||
* particular, those Ranges are still live and will see any entries added
|
||||
* after a successful clear().
|
||||
*/
|
||||
bool clear() {
|
||||
if (dataLength != 0) {
|
||||
Data **oldHashTable = hashTable;
|
||||
Data *oldData = data;
|
||||
uint32_t oldDataLength = dataLength;
|
||||
|
||||
hashTable = NULL;
|
||||
if (!init()) {
|
||||
// init() only mutates members on success; see comment above.
|
||||
hashTable = oldHashTable;
|
||||
return false;
|
||||
}
|
||||
|
||||
alloc.free_(oldHashTable);
|
||||
freeData(oldData, oldDataLength);
|
||||
for (Range *r = ranges; r; r = r->next)
|
||||
r->onClear();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(hashTable);
|
||||
MOZ_ASSERT(data);
|
||||
MOZ_ASSERT(dataLength == 0);
|
||||
MOZ_ASSERT(liveCount == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ranges are used to iterate over OrderedHashTables.
|
||||
*
|
||||
@ -337,6 +375,12 @@ class OrderedHashTable
|
||||
i = count;
|
||||
}
|
||||
|
||||
/* The hash table calls this when cleared. */
|
||||
void onClear() {
|
||||
MOZ_ASSERT(valid());
|
||||
i = count = 0;
|
||||
}
|
||||
|
||||
bool valid() const {
|
||||
return next != this;
|
||||
}
|
||||
@ -476,9 +520,13 @@ class OrderedHashTable
|
||||
return 1 << (HashNumberSizeBits - hashShift);
|
||||
}
|
||||
|
||||
void freeData(Data *data, uint32_t length) {
|
||||
static void destroyData(Data *data, uint32_t length) {
|
||||
for (Data *p = data + length; p != data; )
|
||||
(--p)->~Data();
|
||||
}
|
||||
|
||||
void freeData(Data *data, uint32_t length) {
|
||||
destroyData(data, length);
|
||||
alloc.free_(data);
|
||||
}
|
||||
|
||||
@ -642,6 +690,7 @@ class OrderedHashMap
|
||||
Entry *get(const Key &key) { return impl.get(key); }
|
||||
bool put(const Key &key, const Value &value) { return impl.put(Entry(key, value)); }
|
||||
bool remove(const Key &key, bool *foundp) { return impl.remove(key, foundp); }
|
||||
bool clear() { return impl.clear(); }
|
||||
};
|
||||
|
||||
template <class T, class OrderedHashPolicy, class AllocPolicy>
|
||||
@ -668,6 +717,7 @@ class OrderedHashSet
|
||||
Range all() { return impl.all(); }
|
||||
bool put(const T &value) { return impl.put(value); }
|
||||
bool remove(const T &value, bool *foundp) { return impl.remove(value, foundp); }
|
||||
bool clear() { return impl.clear(); }
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
@ -904,6 +954,7 @@ JSFunctionSpec MapObject::methods[] = {
|
||||
JS_FN("set", set, 2, 0),
|
||||
JS_FN("delete", delete_, 1, 0),
|
||||
JS_FN("iterator", iterator, 0, 0),
|
||||
JS_FN("clear", clear, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
@ -1147,8 +1198,10 @@ MapObject::delete_impl(JSContext *cx, CallArgs args)
|
||||
ValueMap &map = extract(args);
|
||||
ARG0_KEY(cx, args, key);
|
||||
bool found;
|
||||
if (!map.remove(key, &found))
|
||||
if (!map.remove(key, &found)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
args.rval().setBoolean(found);
|
||||
return true;
|
||||
}
|
||||
@ -1179,6 +1232,25 @@ MapObject::iterator(JSContext *cx, unsigned argc, Value *vp)
|
||||
return CallNonGenericMethod(cx, is, iterator_impl, args);
|
||||
}
|
||||
|
||||
bool
|
||||
MapObject::clear_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
Rooted<MapObject*> mapobj(cx, &args.thisv().toObject().asMap());
|
||||
if (!mapobj->getData()->clear()) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
MapObject::clear(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod(cx, is, clear_impl, args);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_InitMapClass(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
@ -1344,6 +1416,7 @@ JSFunctionSpec SetObject::methods[] = {
|
||||
JS_FN("add", add, 1, 0),
|
||||
JS_FN("delete", delete_, 1, 0),
|
||||
JS_FN("iterator", iterator, 0, 0),
|
||||
JS_FN("clear", clear, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
@ -1488,8 +1561,10 @@ SetObject::delete_impl(JSContext *cx, CallArgs args)
|
||||
ValueSet &set = extract(args);
|
||||
ARG0_KEY(cx, args, key);
|
||||
bool found;
|
||||
if (!set.remove(key, &found))
|
||||
if (!set.remove(key, &found)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
args.rval().setBoolean(found);
|
||||
return true;
|
||||
}
|
||||
@ -1520,6 +1595,25 @@ SetObject::iterator(JSContext *cx, unsigned argc, Value *vp)
|
||||
return CallNonGenericMethod(cx, is, iterator_impl, args);
|
||||
}
|
||||
|
||||
bool
|
||||
SetObject::clear_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
Rooted<SetObject*> setobj(cx, &args.thisv().toObject().asSet());
|
||||
if (!setobj->getData()->clear()) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
SetObject::clear(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod(cx, is, clear_impl, args);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_InitSetClass(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
|
@ -105,6 +105,8 @@ class MapObject : public JSObject {
|
||||
static JSBool delete_(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool iterator_impl(JSContext *cx, CallArgs args);
|
||||
static JSBool iterator(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool clear_impl(JSContext *cx, CallArgs args);
|
||||
static JSBool clear(JSContext *cx, unsigned argc, Value *vp);
|
||||
};
|
||||
|
||||
class SetObject : public JSObject {
|
||||
@ -131,6 +133,8 @@ class SetObject : public JSObject {
|
||||
static JSBool delete_(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool iterator_impl(JSContext *cx, CallArgs args);
|
||||
static JSBool iterator(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool clear_impl(JSContext *cx, CallArgs args);
|
||||
static JSBool clear(JSContext *cx, unsigned argc, Value *vp);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
8
js/src/jit-test/tests/collections/Map-clear-1.js
Normal file
8
js/src/jit-test/tests/collections/Map-clear-1.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Clearing an empty Map has no effect.
|
||||
|
||||
var m = Map();
|
||||
for (var i = 0; i < 2; i++) {
|
||||
m.clear();
|
||||
assertEq(m.size(), 0);
|
||||
assertEq(m.has(undefined), false);
|
||||
}
|
17
js/src/jit-test/tests/collections/Map-clear-2.js
Normal file
17
js/src/jit-test/tests/collections/Map-clear-2.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Clearing a Map removes its entries; the Map remains usable afterwards.
|
||||
|
||||
var m = Map([["a", "b"], ["b", "c"]]);
|
||||
assertEq(m.size(), 2);
|
||||
m.clear();
|
||||
assertEq(m.size(), 0);
|
||||
assertEq(m.has("a"), false);
|
||||
assertEq(m.get("a"), undefined);
|
||||
assertEq(m.delete("a"), false);
|
||||
assertEq(m.has("b"), false);
|
||||
for (var pair of m)
|
||||
throw "FAIL"; // shouldn't be any pairs
|
||||
|
||||
m.set("c", "d");
|
||||
assertEq(m.size(), 1);
|
||||
assertEq(m.has("a"), false);
|
||||
assertEq(m.has("b"), false);
|
10
js/src/jit-test/tests/collections/Map-clear-3.js
Normal file
10
js/src/jit-test/tests/collections/Map-clear-3.js
Normal file
@ -0,0 +1,10 @@
|
||||
// Clearing a Map with a nontrivial number of elements works.
|
||||
|
||||
var m = Map();
|
||||
for (var i = 0; i < 100; i++)
|
||||
m.set(i, i);
|
||||
assertEq(m.size(), i);
|
||||
m.clear();
|
||||
assertEq(m.size(), 0);
|
||||
m.set("a", 1);
|
||||
assertEq(m.get("a"), 1);
|
10
js/src/jit-test/tests/collections/Map-clear-4.js
Normal file
10
js/src/jit-test/tests/collections/Map-clear-4.js
Normal file
@ -0,0 +1,10 @@
|
||||
// Clearing a Map after deleting some entries works.
|
||||
|
||||
var m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
|
||||
for (var [k, v] of m)
|
||||
if (k !== "c")
|
||||
m.delete(k);
|
||||
m.clear();
|
||||
assertEq(m.size(), 0);
|
||||
assertEq(m.has("c"), false);
|
||||
assertEq(m.has("d"), false);
|
14
js/src/jit-test/tests/collections/Map-clear-5.js
Normal file
14
js/src/jit-test/tests/collections/Map-clear-5.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Map.clear is unaffected by deleting/monkeypatching Map.prototype.{delete,iterator}.
|
||||
|
||||
var data = [["a", 1], ["b", 2]];
|
||||
var m1 = Map(data), m2 = Map(data);
|
||||
|
||||
delete Map.prototype.delete;
|
||||
delete Map.prototype.iterator;
|
||||
m1.clear();
|
||||
assertEq(m1.size(), 0);
|
||||
|
||||
Map.prototype.delete = function () { throw "FAIL"; };
|
||||
Map.prototype.iterator = function () { throw "FAIL"; };
|
||||
m2.clear();
|
||||
assertEq(m2.size(), 0);
|
6
js/src/jit-test/tests/collections/Map-clear-6.js
Normal file
6
js/src/jit-test/tests/collections/Map-clear-6.js
Normal file
@ -0,0 +1,6 @@
|
||||
// Clearing a Map doesn't affect expando properties.
|
||||
|
||||
var m = Map();
|
||||
m.x = 3;
|
||||
m.clear();
|
||||
assertEq(m.x, 3);
|
12
js/src/jit-test/tests/collections/Map-clear-gc.js
Normal file
12
js/src/jit-test/tests/collections/Map-clear-gc.js
Normal file
@ -0,0 +1,12 @@
|
||||
// Clearing a Map removes any strong references to its keys and values.
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
|
||||
var m = Map();
|
||||
var k = {}, v = {};
|
||||
m.set(k, v);
|
||||
assertEq(referencesVia(m, "key", k), true);
|
||||
assertEq(referencesVia(m, "value", v), true);
|
||||
m.clear();
|
||||
assertEq(referencesVia(m, "key", k), false);
|
||||
assertEq(referencesVia(m, "value", v), false);
|
23
js/src/jit-test/tests/collections/Map-clear-iterators-1.js
Normal file
23
js/src/jit-test/tests/collections/Map-clear-iterators-1.js
Normal file
@ -0,0 +1,23 @@
|
||||
// A Map iterator does not visit entries removed by clear().
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var m = Map();
|
||||
var it = m.iterator();
|
||||
m.clear();
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
||||
|
||||
m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
|
||||
it = m.iterator();
|
||||
assertEq(it.next()[0], "a");
|
||||
m.clear();
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
||||
|
||||
var log = "";
|
||||
m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
|
||||
for (var [k, v] of m) {
|
||||
log += k + v;
|
||||
if (k == "b")
|
||||
m.clear();
|
||||
}
|
||||
assertEq(log, "a1b2");
|
15
js/src/jit-test/tests/collections/Map-clear-iterators-2.js
Normal file
15
js/src/jit-test/tests/collections/Map-clear-iterators-2.js
Normal file
@ -0,0 +1,15 @@
|
||||
// A Map iterator continues to visit entries added after a clear().
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var m = Map([["a", 1]]);
|
||||
var it = m.iterator();
|
||||
assertEq(it.next()[0], "a");
|
||||
m.clear();
|
||||
m.set("b", 2);
|
||||
var pair = it.next()
|
||||
assertEq(pair[0], "b");
|
||||
assertEq(pair[1], 2);
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
||||
|
||||
|
10
js/src/jit-test/tests/collections/Map-iterators-3.js
Normal file
10
js/src/jit-test/tests/collections/Map-iterators-3.js
Normal file
@ -0,0 +1,10 @@
|
||||
// A closed Map iterator does not visit new entries added after a clear().
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var m = Map();
|
||||
var it = m.iterator();
|
||||
assertThrowsValue(it.next.bind(it), StopIteration); // close the iterator
|
||||
m.clear();
|
||||
m.set("a", 1);
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
@ -32,3 +32,4 @@ checkMethod("get", 1);
|
||||
checkMethod("has", 1);
|
||||
checkMethod("set", 2);
|
||||
checkMethod("delete", 1);
|
||||
checkMethod("clear", 0);
|
||||
|
@ -14,6 +14,7 @@ function test(obj) {
|
||||
testcase(obj, Map.prototype.has, "x");
|
||||
testcase(obj, Map.prototype.set, "x", 1);
|
||||
testcase(obj, Map.prototype.delete, "x");
|
||||
testcase(obj, Map.prototype.clear);
|
||||
}
|
||||
|
||||
test(Map.prototype);
|
||||
|
8
js/src/jit-test/tests/collections/Set-clear-1.js
Normal file
8
js/src/jit-test/tests/collections/Set-clear-1.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Clearing an empty Set has no effect.
|
||||
|
||||
var s = Set();
|
||||
for (var i = 0; i < 2; i++) {
|
||||
s.clear();
|
||||
assertEq(s.size(), 0);
|
||||
assertEq(s.has(undefined), false);
|
||||
}
|
16
js/src/jit-test/tests/collections/Set-clear-2.js
Normal file
16
js/src/jit-test/tests/collections/Set-clear-2.js
Normal file
@ -0,0 +1,16 @@
|
||||
// Clearing a Set removes its elements; the Set remains usable afterwards.
|
||||
|
||||
var s = Set(["x", "y", "z", "z", "y"]);
|
||||
assertEq(s.size(), 3);
|
||||
s.clear();
|
||||
assertEq(s.size(), 0);
|
||||
assertEq(s.has("x"), false);
|
||||
assertEq(s.delete("x"), false);
|
||||
assertEq(s.has("z"), false);
|
||||
for (var v of s)
|
||||
throw "FAIL"; // shouldn't be any elements
|
||||
|
||||
s.add("y");
|
||||
assertEq(s.size(), 1);
|
||||
assertEq(s.has("x"), false);
|
||||
assertEq(s.has("z"), false);
|
10
js/src/jit-test/tests/collections/Set-clear-3.js
Normal file
10
js/src/jit-test/tests/collections/Set-clear-3.js
Normal file
@ -0,0 +1,10 @@
|
||||
// Clearing a Set with a nontrivial number of elements works.
|
||||
|
||||
var s = Set();
|
||||
for (var i = 0; i < 100; i++)
|
||||
s.add(i);
|
||||
assertEq(s.size(), i);
|
||||
s.clear();
|
||||
assertEq(s.size(), 0);
|
||||
s.add(12);
|
||||
assertEq(s.has(12), true);
|
10
js/src/jit-test/tests/collections/Set-clear-4.js
Normal file
10
js/src/jit-test/tests/collections/Set-clear-4.js
Normal file
@ -0,0 +1,10 @@
|
||||
// Clearing a Set after deleting some entries works.
|
||||
|
||||
var s = Set(["a", "b", "c", "d"]);
|
||||
for (var v of s)
|
||||
if (v !== "c")
|
||||
s.delete(v);
|
||||
s.clear();
|
||||
assertEq(s.size(), 0);
|
||||
assertEq(s.has("c"), false);
|
||||
assertEq(s.has("d"), false);
|
14
js/src/jit-test/tests/collections/Set-clear-5.js
Normal file
14
js/src/jit-test/tests/collections/Set-clear-5.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Set.clear is unaffected by deleting/monkeypatching Set.prototype.{delete,iterator}.
|
||||
|
||||
var data = ["a", 1, {}];
|
||||
var s1 = Set(data), s2 = Set(data);
|
||||
|
||||
delete Set.prototype.delete;
|
||||
delete Set.prototype.iterator;
|
||||
s1.clear();
|
||||
assertEq(s1.size(), 0);
|
||||
|
||||
Set.prototype.delete = function () { throw "FAIL"; };
|
||||
Set.prototype.iterator = function () { throw "FAIL"; };
|
||||
s2.clear();
|
||||
assertEq(s2.size(), 0);
|
6
js/src/jit-test/tests/collections/Set-clear-6.js
Normal file
6
js/src/jit-test/tests/collections/Set-clear-6.js
Normal file
@ -0,0 +1,6 @@
|
||||
// Clearing a Set doesn't affect expando properties.
|
||||
|
||||
var s = Set();
|
||||
s.x = 3;
|
||||
s.clear();
|
||||
assertEq(s.x, 3);
|
10
js/src/jit-test/tests/collections/Set-clear-gc.js
Normal file
10
js/src/jit-test/tests/collections/Set-clear-gc.js
Normal file
@ -0,0 +1,10 @@
|
||||
// Clearing a Set removes any strong references to its elements.
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
|
||||
var s = Set();
|
||||
var obj = {};
|
||||
s.add(obj);
|
||||
assertEq(referencesVia(s, "key", obj), true);
|
||||
s.clear();
|
||||
assertEq(referencesVia(s, "key", obj), false);
|
23
js/src/jit-test/tests/collections/Set-clear-iterators-1.js
Normal file
23
js/src/jit-test/tests/collections/Set-clear-iterators-1.js
Normal file
@ -0,0 +1,23 @@
|
||||
// A Set iterator does not visit entries removed by clear().
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var s = Set();
|
||||
var it = s.iterator();
|
||||
s.clear();
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
||||
|
||||
s = Set(["a", "b", "c", "d"]);
|
||||
it = s.iterator();
|
||||
assertEq(it.next()[0], "a");
|
||||
s.clear();
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
||||
|
||||
var log = "";
|
||||
s = Set(["a", "b", "c", "d"]);
|
||||
for (var v of s) {
|
||||
log += v;
|
||||
if (v == "b")
|
||||
s.clear();
|
||||
}
|
||||
assertEq(log, "ab");
|
11
js/src/jit-test/tests/collections/Set-clear-iterators-2.js
Normal file
11
js/src/jit-test/tests/collections/Set-clear-iterators-2.js
Normal file
@ -0,0 +1,11 @@
|
||||
// A Set iterator continues to visit entries added after a clear().
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var s = Set(["a"]);
|
||||
var it = s.iterator();
|
||||
assertEq(it.next(), "a");
|
||||
s.clear();
|
||||
s.add("b");
|
||||
assertEq(it.next(), "b");
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
10
js/src/jit-test/tests/collections/Set-clear-iterators-3.js
Normal file
10
js/src/jit-test/tests/collections/Set-clear-iterators-3.js
Normal file
@ -0,0 +1,10 @@
|
||||
// A closed Set iterator does not visit new entries added after a clear().
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var s = Set();
|
||||
var it = s.iterator();
|
||||
assertThrowsValue(it.next.bind(it), StopIteration); // close the iterator
|
||||
s.clear();
|
||||
s.add("a");
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
@ -30,3 +30,4 @@ function checkMethod(name, arity) {
|
||||
checkMethod("has", 1);
|
||||
checkMethod("add", 1);
|
||||
checkMethod("delete", 1);
|
||||
checkMethod("clear", 0);
|
||||
|
@ -12,6 +12,7 @@ function test(obj) {
|
||||
testcase(obj, Set.prototype.has, 12);
|
||||
testcase(obj, Set.prototype.add, 12);
|
||||
testcase(obj, Set.prototype.delete, 12);
|
||||
testcase(obj, Set.prototype.clear);
|
||||
}
|
||||
|
||||
test(Set.prototype);
|
||||
|
Loading…
Reference in New Issue
Block a user