Implement Harmony sets and maps.

This implementation extends the internal ObjectHashTable to be able to
hold arbitrary objects (e.g. Smis, Strings, ...) as keys by applying
specialized hashing functions to primitive types. Equality of keys is
defined using the internal SameValue function.

R=rossberg@chromium.org
BUG=v8:1622
TEST=mjsunit/harmony/collections

Review URL: http://codereview.chromium.org/8372027

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9777 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 0442e080
......@@ -321,7 +321,7 @@ debug-debugger.js
EXPERIMENTAL_LIBRARY_FILES = '''
proxy.js
weakmap.js
collection.js
'''.split()
......
......@@ -1243,12 +1243,26 @@ void Genesis::InitializeExperimentalGlobal() {
Handle<JSObject> global = Handle<JSObject>(global_context()->global());
// TODO(mstarzinger): Move this into Genesis::InitializeGlobal once we no
// longer need to live behind a flag, so WeakMap gets added to the snapshot.
if (FLAG_harmony_weakmaps) { // -- W e a k M a p
Handle<JSObject> prototype =
factory()->NewJSObject(isolate()->object_function(), TENURED);
InstallFunction(global, "WeakMap", JS_WEAK_MAP_TYPE, JSWeakMap::kSize,
prototype, Builtins::kIllegal, true);
// longer need to live behind a flag, so functions get added to the snapshot.
if (FLAG_harmony_collections) {
{ // -- S e t
Handle<JSObject> prototype =
factory()->NewJSObject(isolate()->object_function(), TENURED);
InstallFunction(global, "Set", JS_SET_TYPE, JSSet::kSize,
prototype, Builtins::kIllegal, true);
}
{ // -- M a p
Handle<JSObject> prototype =
factory()->NewJSObject(isolate()->object_function(), TENURED);
InstallFunction(global, "Map", JS_MAP_TYPE, JSMap::kSize,
prototype, Builtins::kIllegal, true);
}
{ // -- W e a k M a p
Handle<JSObject> prototype =
factory()->NewJSObject(isolate()->object_function(), TENURED);
InstallFunction(global, "WeakMap", JS_WEAK_MAP_TYPE, JSWeakMap::kSize,
prototype, Builtins::kIllegal, true);
}
}
}
......@@ -1759,9 +1773,9 @@ bool Genesis::InstallExperimentalNatives() {
"native proxy.js") == 0) {
if (!CompileExperimentalBuiltin(isolate(), i)) return false;
}
if (FLAG_harmony_weakmaps &&
if (FLAG_harmony_collections &&
strcmp(ExperimentalNatives::GetScriptName(i).start(),
"native weakmap.js") == 0) {
"native collection.js") == 0) {
if (!CompileExperimentalBuiltin(isolate(), i)) return false;
}
}
......
......@@ -26,12 +26,69 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file relies on the fact that the following declaration has been made
// in runtime.js:
// const $Object = global.Object;
const $Set = global.Set;
const $Map = global.Map;
const $WeakMap = global.WeakMap;
// -------------------------------------------------------------------
//-------------------------------------------------------------------
function SetConstructor() {
if (%_IsConstructCall()) {
%SetInitialize(this);
} else {
return new $Set();
}
}
function SetAdd(key) {
return %SetAdd(this, key);
}
function SetHas(key) {
return %SetHas(this, key);
}
function SetDelete(key) {
return %SetDelete(this, key);
}
function MapConstructor() {
if (%_IsConstructCall()) {
%MapInitialize(this);
} else {
return new $Map();
}
}
function MapGet(key) {
return %MapGet(this, key);
}
function MapSet(key, value) {
return %MapSet(this, key, value);
}
function MapHas(key) {
return !IS_UNDEFINED(%MapGet(this, key));
}
function MapDelete(key) {
if (!IS_UNDEFINED(%MapGet(this, key))) {
%MapSet(this, key, void 0);
return true;
} else {
return false;
}
}
function WeakMapConstructor() {
if (%_IsConstructCall()) {
......@@ -82,6 +139,30 @@ function WeakMapDelete(key) {
(function () {
%CheckIsBootstrapping();
// Set up the Set and Map constructor function.
%SetCode($Set, SetConstructor);
%SetCode($Map, MapConstructor);
// Set up the constructor property on the Set and Map prototype object.
%SetProperty($Set.prototype, "constructor", $Set, DONT_ENUM);
%SetProperty($Map.prototype, "constructor", $Map, DONT_ENUM);
// Set up the non-enumerable functions on the Set prototype object.
InstallFunctionsOnHiddenPrototype($Set.prototype, DONT_ENUM, $Array(
"add", SetAdd,
"has", SetHas,
"delete", SetDelete
));
// Set up the non-enumerable functions on the Map prototype object.
InstallFunctionsOnHiddenPrototype($Map.prototype, DONT_ENUM, $Array(
"get", MapGet,
"set", MapSet,
"has", MapHas,
"delete", MapDelete
));
// Set up the WeakMap constructor function.
%SetCode($WeakMap, WeakMapConstructor);
......
......@@ -85,6 +85,14 @@ Handle<NumberDictionary> Factory::NewNumberDictionary(int at_least_space_for) {
}
Handle<ObjectHashSet> Factory::NewObjectHashSet(int at_least_space_for) {
ASSERT(0 <= at_least_space_for);
CALL_HEAP_FUNCTION(isolate(),
ObjectHashSet::Allocate(at_least_space_for),
ObjectHashSet);
}
Handle<ObjectHashTable> Factory::NewObjectHashTable(int at_least_space_for) {
ASSERT(0 <= at_least_space_for);
CALL_HEAP_FUNCTION(isolate(),
......
......@@ -58,6 +58,8 @@ class Factory {
Handle<StringDictionary> NewStringDictionary(int at_least_space_for);
Handle<ObjectHashSet> NewObjectHashSet(int at_least_space_for);
Handle<ObjectHashTable> NewObjectHashTable(int at_least_space_for);
Handle<DescriptorArray> NewDescriptorArray(int number_of_descriptors);
......
......@@ -100,7 +100,8 @@ private:
DEFINE_bool(harmony_typeof, false, "enable harmony semantics for typeof")
DEFINE_bool(harmony_scoping, false, "enable harmony block scoping")
DEFINE_bool(harmony_proxies, false, "enable harmony proxies")
DEFINE_bool(harmony_weakmaps, false, "enable harmony weak maps")
DEFINE_bool(harmony_collections, false,
"enable harmony collections (sets, maps, and weak maps)")
DEFINE_bool(harmony, false, "enable all harmony features")
// Flags for experimental implementation features.
......
......@@ -891,8 +891,24 @@ Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
}
Handle<ObjectHashSet> ObjectHashSetAdd(Handle<ObjectHashSet> table,
Handle<Object> key) {
CALL_HEAP_FUNCTION(table->GetIsolate(),
table->Add(*key),
ObjectHashSet);
}
Handle<ObjectHashSet> ObjectHashSetRemove(Handle<ObjectHashSet> table,
Handle<Object> key) {
CALL_HEAP_FUNCTION(table->GetIsolate(),
table->Remove(*key),
ObjectHashSet);
}
Handle<ObjectHashTable> PutIntoObjectHashTable(Handle<ObjectHashTable> table,
Handle<JSReceiver> key,
Handle<Object> key,
Handle<Object> value) {
CALL_HEAP_FUNCTION(table->GetIsolate(),
table->Put(*key, *value),
......
......@@ -342,8 +342,14 @@ Handle<Object> SetPrototype(Handle<JSFunction> function,
Handle<Object> PreventExtensions(Handle<JSObject> object);
Handle<ObjectHashSet> ObjectHashSetAdd(Handle<ObjectHashSet> table,
Handle<Object> key);
Handle<ObjectHashSet> ObjectHashSetRemove(Handle<ObjectHashSet> table,
Handle<Object> key);
Handle<ObjectHashTable> PutIntoObjectHashTable(Handle<ObjectHashTable> table,
Handle<JSReceiver> key,
Handle<Object> key,
Handle<Object> value);
class NoHandleAllocation BASE_EMBEDDED {
......
......@@ -156,6 +156,12 @@ void HeapObject::HeapObjectVerify() {
case JS_ARRAY_TYPE:
JSArray::cast(this)->JSArrayVerify();
break;
case JS_SET_TYPE:
JSSet::cast(this)->JSSetVerify();
break;
case JS_MAP_TYPE:
JSMap::cast(this)->JSMapVerify();
break;
case JS_WEAK_MAP_TYPE:
JSWeakMap::cast(this)->JSWeakMapVerify();
break;
......@@ -500,6 +506,22 @@ void JSArray::JSArrayVerify() {
}
void JSSet::JSSetVerify() {
CHECK(IsJSSet());
JSObjectVerify();
VerifyHeapPointer(table());
ASSERT(table()->IsHashTable() || table()->IsUndefined());
}
void JSMap::JSMapVerify() {
CHECK(IsJSMap());
JSObjectVerify();
VerifyHeapPointer(table());
ASSERT(table()->IsHashTable() || table()->IsUndefined());
}
void JSWeakMap::JSWeakMapVerify() {
CHECK(IsJSWeakMap());
JSObjectVerify();
......
......@@ -67,6 +67,13 @@ PropertyDetails PropertyDetails::AsDeleted() {
}
#define TYPE_CHECKER(type, instancetype) \
bool Object::Is##type() { \
return Object::IsHeapObject() && \
HeapObject::cast(this)->map()->instance_type() == instancetype; \
}
#define CAST_ACCESSOR(type) \
type* type::cast(Object* object) { \
ASSERT(object->Is##type()); \
......@@ -152,10 +159,7 @@ bool Object::NonFailureIsHeapObject() {
}
bool Object::IsHeapNumber() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == HEAP_NUMBER_TYPE;
}
TYPE_CHECKER(HeapNumber, HEAP_NUMBER_TYPE)
bool Object::IsString() {
......@@ -408,16 +412,8 @@ bool Object::IsNumber() {
}
bool Object::IsByteArray() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == BYTE_ARRAY_TYPE;
}
bool Object::IsFreeSpace() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == FREE_SPACE_TYPE;
}
TYPE_CHECKER(ByteArray, BYTE_ARRAY_TYPE)
TYPE_CHECKER(FreeSpace, FREE_SPACE_TYPE)
bool Object::IsFiller() {
......@@ -427,11 +423,7 @@ bool Object::IsFiller() {
}
bool Object::IsExternalPixelArray() {
return Object::IsHeapObject() &&
HeapObject::cast(this)->map()->instance_type() ==
EXTERNAL_PIXEL_ARRAY_TYPE;
}
TYPE_CHECKER(ExternalPixelArray, EXTERNAL_PIXEL_ARRAY_TYPE)
bool Object::IsExternalArray() {
......@@ -444,60 +436,14 @@ bool Object::IsExternalArray() {
}
bool Object::IsExternalByteArray() {
return Object::IsHeapObject() &&
HeapObject::cast(this)->map()->instance_type() ==
EXTERNAL_BYTE_ARRAY_TYPE;
}
bool Object::IsExternalUnsignedByteArray() {
return Object::IsHeapObject() &&
HeapObject::cast(this)->map()->instance_type() ==
EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE;
}
bool Object::IsExternalShortArray() {
return Object::IsHeapObject() &&
HeapObject::cast(this)->map()->instance_type() ==
EXTERNAL_SHORT_ARRAY_TYPE;
}
bool Object::IsExternalUnsignedShortArray() {
return Object::IsHeapObject() &&
HeapObject::cast(this)->map()->instance_type() ==
EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE;
}
bool Object::IsExternalIntArray() {
return Object::IsHeapObject() &&
HeapObject::cast(this)->map()->instance_type() ==
EXTERNAL_INT_ARRAY_TYPE;
}
bool Object::IsExternalUnsignedIntArray() {
return Object::IsHeapObject() &&
HeapObject::cast(this)->map()->instance_type() ==
EXTERNAL_UNSIGNED_INT_ARRAY_TYPE;
}
bool Object::IsExternalFloatArray() {
return Object::IsHeapObject() &&
HeapObject::cast(this)->map()->instance_type() ==
EXTERNAL_FLOAT_ARRAY_TYPE;
}
bool Object::IsExternalDoubleArray() {
return Object::IsHeapObject() &&
HeapObject::cast(this)->map()->instance_type() ==
EXTERNAL_DOUBLE_ARRAY_TYPE;
}
TYPE_CHECKER(ExternalByteArray, EXTERNAL_BYTE_ARRAY_TYPE)
TYPE_CHECKER(ExternalUnsignedByteArray, EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE)
TYPE_CHECKER(ExternalShortArray, EXTERNAL_SHORT_ARRAY_TYPE)
TYPE_CHECKER(ExternalUnsignedShortArray, EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE)
TYPE_CHECKER(ExternalIntArray, EXTERNAL_INT_ARRAY_TYPE)
TYPE_CHECKER(ExternalUnsignedIntArray, EXTERNAL_UNSIGNED_INT_ARRAY_TYPE)
TYPE_CHECKER(ExternalFloatArray, EXTERNAL_FLOAT_ARRAY_TYPE)
TYPE_CHECKER(ExternalDoubleArray, EXTERNAL_DOUBLE_ARRAY_TYPE)
bool MaybeObject::IsFailure() {
......@@ -554,42 +500,14 @@ bool Object::IsJSProxy() {
}
bool Object::IsJSFunctionProxy() {
return Object::IsHeapObject() &&
HeapObject::cast(this)->map()->instance_type() == JS_FUNCTION_PROXY_TYPE;
}
bool Object::IsJSWeakMap() {
return Object::IsJSObject() &&
HeapObject::cast(this)->map()->instance_type() == JS_WEAK_MAP_TYPE;
}
bool Object::IsJSContextExtensionObject() {
return IsHeapObject()
&& (HeapObject::cast(this)->map()->instance_type() ==
JS_CONTEXT_EXTENSION_OBJECT_TYPE);
}
bool Object::IsMap() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == MAP_TYPE;
}
bool Object::IsFixedArray() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == FIXED_ARRAY_TYPE;
}
bool Object::IsFixedDoubleArray() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() ==
FIXED_DOUBLE_ARRAY_TYPE;
}
TYPE_CHECKER(JSFunctionProxy, JS_FUNCTION_PROXY_TYPE)
TYPE_CHECKER(JSSet, JS_SET_TYPE)
TYPE_CHECKER(JSMap, JS_MAP_TYPE)
TYPE_CHECKER(JSWeakMap, JS_WEAK_MAP_TYPE)
TYPE_CHECKER(JSContextExtensionObject, JS_CONTEXT_EXTENSION_OBJECT_TYPE)
TYPE_CHECKER(Map, MAP_TYPE)
TYPE_CHECKER(FixedArray, FIXED_ARRAY_TYPE)
TYPE_CHECKER(FixedDoubleArray, FIXED_DOUBLE_ARRAY_TYPE)
bool Object::IsDescriptorArray() {
......@@ -652,10 +570,7 @@ bool Object::IsSerializedScopeInfo() {
}
bool Object::IsJSFunction() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == JS_FUNCTION_TYPE;
}
TYPE_CHECKER(JSFunction, JS_FUNCTION_TYPE)
template <> inline bool Is<JSFunction>(Object* obj) {
......@@ -663,43 +578,12 @@ template <> inline bool Is<JSFunction>(Object* obj) {
}
bool Object::IsCode() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == CODE_TYPE;
}
bool Object::IsOddball() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == ODDBALL_TYPE;
}
bool Object::IsJSGlobalPropertyCell() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type()
== JS_GLOBAL_PROPERTY_CELL_TYPE;
}
bool Object::IsSharedFunctionInfo() {
return Object::IsHeapObject() &&
(HeapObject::cast(this)->map()->instance_type() ==
SHARED_FUNCTION_INFO_TYPE);
}
bool Object::IsJSValue() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == JS_VALUE_TYPE;
}
bool Object::IsJSMessageObject() {
return Object::IsHeapObject()
&& (HeapObject::cast(this)->map()->instance_type() ==
JS_MESSAGE_OBJECT_TYPE);
}
TYPE_CHECKER(Code, CODE_TYPE)
TYPE_CHECKER(Oddball, ODDBALL_TYPE)
TYPE_CHECKER(JSGlobalPropertyCell, JS_GLOBAL_PROPERTY_CELL_TYPE)
TYPE_CHECKER(SharedFunctionInfo, SHARED_FUNCTION_INFO_TYPE)
TYPE_CHECKER(JSValue, JS_VALUE_TYPE)
TYPE_CHECKER(JSMessageObject, JS_MESSAGE_OBJECT_TYPE)
bool Object::IsStringWrapper() {
......@@ -707,10 +591,7 @@ bool Object::IsStringWrapper() {
}
bool Object::IsForeign() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == FOREIGN_TYPE;
}
TYPE_CHECKER(Foreign, FOREIGN_TYPE)
bool Object::IsBoolean() {
......@@ -719,16 +600,8 @@ bool Object::IsBoolean() {
}
bool Object::IsJSArray() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == JS_ARRAY_TYPE;
}
bool Object::IsJSRegExp() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == JS_REGEXP_TYPE;
}
TYPE_CHECKER(JSArray, JS_ARRAY_TYPE)
TYPE_CHECKER(JSRegExp, JS_REGEXP_TYPE)
template <> inline bool Is<JSArray>(Object* obj) {
......@@ -831,18 +704,8 @@ bool Object::IsGlobalObject() {
}
bool Object::IsJSGlobalObject() {
return IsHeapObject() &&
(HeapObject::cast(this)->map()->instance_type() ==
JS_GLOBAL_OBJECT_TYPE);
}
bool Object::IsJSBuiltinsObject() {
return IsHeapObject() &&
(HeapObject::cast(this)->map()->instance_type() ==
JS_BUILTINS_OBJECT_TYPE);
}
TYPE_CHECKER(JSGlobalObject, JS_GLOBAL_OBJECT_TYPE)
TYPE_CHECKER(JSBuiltinsObject, JS_BUILTINS_OBJECT_TYPE)
bool Object::IsUndetectableObject() {
......@@ -2184,6 +2047,8 @@ CAST_ACCESSOR(JSArray)
CAST_ACCESSOR(JSRegExp)
CAST_ACCESSOR(JSProxy)
CAST_ACCESSOR(JSFunctionProxy)
CAST_ACCESSOR(JSSet)
CAST_ACCESSOR(JSMap)
CAST_ACCESSOR(JSWeakMap)
CAST_ACCESSOR(Foreign)
CAST_ACCESSOR(ByteArray)
......@@ -3971,6 +3836,8 @@ void JSProxy::InitializeBody(int object_size, Object* value) {
}
ACCESSORS(JSSet, table, Object, kTableOffset)
ACCESSORS(JSMap, table, Object, kTableOffset)
ACCESSORS(JSWeakMap, table, Object, kTableOffset)
ACCESSORS(JSWeakMap, next, Object, kNextOffset)
......@@ -4559,27 +4426,31 @@ MaybeObject* StringDictionaryShape::AsObject(String* key) {
}
bool ObjectHashTableShape::IsMatch(JSReceiver* key, Object* other) {
return key == JSReceiver::cast(other);
template <int entrysize>
bool ObjectHashTableShape<entrysize>::IsMatch(Object* key, Object* other) {
return key->SameValue(other);
}
uint32_t ObjectHashTableShape::Hash(JSReceiver* key) {
MaybeObject* maybe_hash = key->GetIdentityHash(OMIT_CREATION);
ASSERT(!maybe_hash->IsFailure());
return Smi::cast(maybe_hash->ToObjectUnchecked())->value();
template <int entrysize>
uint32_t ObjectHashTableShape<entrysize>::Hash(Object* key) {
ASSERT(!key->IsUndefined() && !key->IsNull());
MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION);
return Smi::cast(maybe_hash->ToObjectChecked())->value();
}
uint32_t ObjectHashTableShape::HashForObject(JSReceiver* key, Object* other) {
MaybeObject* maybe_hash =
JSReceiver::cast(other)->GetIdentityHash(OMIT_CREATION);
ASSERT(!maybe_hash->IsFailure());
return Smi::cast(maybe_hash->ToObjectUnchecked())->value();
template <int entrysize>
uint32_t ObjectHashTableShape<entrysize>::HashForObject(Object* key,
Object* other) {
ASSERT(!other->IsUndefined() && !other->IsNull());
MaybeObject* maybe_hash = other->GetHash(OMIT_CREATION);
return Smi::cast(maybe_hash->ToObjectChecked())->value();
}
MaybeObject* ObjectHashTableShape::AsObject(JSReceiver* key) {
template <int entrysize>
MaybeObject* ObjectHashTableShape<entrysize>::AsObject(Object* key) {
return key;
}
......
......@@ -94,6 +94,16 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
case JS_GLOBAL_PROPERTY_CELL_TYPE:
return kVisitPropertyCell;
case JS_SET_TYPE:
return GetVisitorIdForSize(kVisitStruct,
kVisitStructGeneric,
JSSet::kSize);
case JS_MAP_TYPE:
return GetVisitorIdForSize(kVisitStruct,
kVisitStructGeneric,
JSMap::kSize);
case JS_WEAK_MAP_TYPE:
return kVisitJSWeakMap;
......
......@@ -771,6 +771,49 @@ Object* Object::GetPrototype() {
}
MaybeObject* Object::GetHash(CreationFlag flag) {
// The object is either a number, a string, an odd-ball,
// a real JS object, or a Harmony proxy.
if (IsNumber()) {
uint32_t hash = ComputeLongHash(double_to_uint64(Number()));
return Smi::FromInt(hash & Smi::kMaxValue);
}
if (IsString()) {
uint32_t hash = String::cast(this)->Hash();
return Smi::FromInt(hash);
}
if (IsOddball()) {
uint32_t hash = Oddball::cast(this)->to_string()->Hash();
return Smi::FromInt(hash);
}
if (IsJSReceiver()) {
return JSReceiver::cast(this)->GetIdentityHash(flag);
}
UNREACHABLE();
return Smi::FromInt(0);
}
bool Object::SameValue(Object* other) {
if (other == this) return true;
if (!IsHeapObject() || !other->IsHeapObject()) return false;
// The object is either a number, a string, an odd-ball,
// a real JS object, or a Harmony proxy.
if (IsNumber() && other->IsNumber()) {
double this_value = Number();
double other_value = other->Number();
return (this_value == other_value) ||
(isnan(this_value) && isnan(other_value));
}
if (IsString() && other->IsString()) {
return String::cast(this)->Equals(String::cast(other));
}
return false;
}
void Object::ShortPrint(FILE* out) {
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
......@@ -1343,6 +1386,8 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_VALUE_TYPE:
case JS_ARRAY_TYPE:
case JS_SET_TYPE:
case JS_MAP_TYPE:
case JS_WEAK_MAP_TYPE:
case JS_REGEXP_TYPE:
case JS_GLOBAL_PROXY_TYPE:
......@@ -10983,7 +11028,9 @@ template class HashTable<CompilationCacheShape, HashTableKey*>;
template class HashTable<MapCacheShape, HashTableKey*>;
template class HashTable<ObjectHashTableShape, JSReceiver*>;
template class HashTable<ObjectHashTableShape<1>, Object*>;
template class HashTable<ObjectHashTableShape<2>, Object*>;
template class Dictionary<StringDictionaryShape, String*>;
......@@ -12340,20 +12387,72 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
}
Object* ObjectHashTable::Lookup(JSReceiver* key) {
bool ObjectHashSet::Contains(Object* key) {
// If the object does not have an identity hash, it was never used as a key.
{ MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION);
if (maybe_hash->IsFailure()) return false;
}
return (FindEntry(key) != kNotFound);
}
MaybeObject* ObjectHashSet::Add(Object* key) {
// Make sure the key object has an identity hash code.
int hash;
{ MaybeObject* maybe_hash = key->GetHash(ALLOW_CREATION);
if (maybe_hash->IsFailure()) return maybe_hash;
hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value();
}
int entry = FindEntry(key);
// Check whether key is already present.
if (entry != kNotFound) return this;
// Check whether the hash set should be extended and add entry.
Object* obj;
{ MaybeObject* maybe_obj = EnsureCapacity(1, key);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
ObjectHashSet* table = ObjectHashSet::cast(obj);
entry = table->FindInsertionEntry(hash);
table->set(EntryToIndex(entry), key);
table->ElementAdded();
return table;
}
MaybeObject* ObjectHashSet::Remove(Object* key) {
// If the object does not have an identity hash, it was never used as a key.
MaybeObject* maybe_hash = key->GetIdentityHash(OMIT_CREATION);
if (maybe_hash->IsFailure()) return GetHeap()->undefined_value();
{ MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION);
if (maybe_hash->IsFailure()) return this;
}
int entry = FindEntry(key);
// Check whether key is actually present.
if (entry == kNotFound) return this;
// Remove entry and try to shrink this hash set.
set_null(EntryToIndex(entry));
ElementRemoved();
return Shrink(key);
}
Object* ObjectHashTable::Lookup(Object* key) {
// If the object does not have an identity hash, it was never used as a key.
{ MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION);
if (maybe_hash->IsFailure()) GetHeap()->undefined_value();
}
int entry = FindEntry(key);
if (entry == kNotFound) return GetHeap()->undefined_value();
return get(EntryToIndex(entry) + 1);
}
MaybeObject* ObjectHashTable::Put(JSReceiver* key, Object* value) {
MaybeObject* ObjectHashTable::Put(Object* key, Object* value) {
// Make sure the key object has an identity hash code.
int hash;
{ MaybeObject* maybe_hash = key->GetIdentityHash(ALLOW_CREATION);
{ MaybeObject* maybe_hash = key->GetHash(ALLOW_CREATION);
if (maybe_hash->IsFailure()) return maybe_hash;
hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value();
}
......@@ -12383,7 +12482,7 @@ MaybeObject* ObjectHashTable::Put(JSReceiver* key, Object* value) {
}
void ObjectHashTable::AddEntry(int entry, JSReceiver* key, Object* value) {
void ObjectHashTable::AddEntry(int entry, Object* key, Object* value) {
set(EntryToIndex(entry), key);
set(EntryToIndex(entry) + 1, value);
ElementAdded();
......
......@@ -53,6 +53,8 @@
// - JSReceiver (suitable for property access)
// - JSObject
// - JSArray
// - JSSet
// - JSMap
// - JSWeakMap
// - JSRegExp
// - JSFunction
......@@ -631,6 +633,8 @@ enum InstanceType {
JS_BUILTINS_OBJECT_TYPE,
JS_GLOBAL_PROXY_TYPE,
JS_ARRAY_TYPE,
JS_SET_TYPE,
JS_MAP_TYPE,
JS_WEAK_MAP_TYPE,
JS_REGEXP_TYPE,
......@@ -823,6 +827,8 @@ class MaybeObject BASE_EMBEDDED {
V(JSArray) \
V(JSProxy) \
V(JSFunctionProxy) \
V(JSSet) \
V(JSMap) \
V(JSWeakMap) \
V(JSRegExp) \
V(HashTable) \
......@@ -941,6 +947,16 @@ class Object : public MaybeObject {
// Return the object's prototype (might be Heap::null_value()).
Object* GetPrototype();
// Returns the permanent hash code associated with this object depending on
// the actual object type. Might return a failure in case no hash was
// created yet or GC was caused by creation.
MUST_USE_RESULT MaybeObject* GetHash(CreationFlag flag);
// Checks whether this object has the same value as the given one. This
// function is implemented according to ES5, section 9.12 and can be used
// to implement the Harmony "egal" function.
bool SameValue(Object* other);
// Tries to convert an object to an array index. Returns true and sets
// the output parameter if it succeeds.
inline bool ToArrayIndex(uint32_t* index);
......@@ -1393,7 +1409,7 @@ class JSReceiver: public HeapObject {
bool skip_hidden_prototypes);
// Retrieves a permanent object identity hash code. The undefined value might
// be returned in case no has been created yet and OMIT_CREATION was used.
// be returned in case no hash was created yet and OMIT_CREATION was used.
inline MUST_USE_RESULT MaybeObject* GetIdentityHash(CreationFlag flag);
// Lookup a property. If found, the result is valid and has
......@@ -2981,20 +2997,41 @@ class NumberDictionary: public Dictionary<NumberDictionaryShape, uint32_t> {
};
template <int entrysize>
class ObjectHashTableShape {
public:
static inline bool IsMatch(JSReceiver* key, Object* other);
static inline uint32_t Hash(JSReceiver* key);
static inline uint32_t HashForObject(JSReceiver* key, Object* object);
MUST_USE_RESULT static inline MaybeObject* AsObject(JSReceiver* key);
static inline bool IsMatch(Object* key, Object* other);
static inline uint32_t Hash(Object* key);
static inline uint32_t HashForObject(Object* key, Object* object);
MUST_USE_RESULT static inline MaybeObject* AsObject(Object* key);
static const int kPrefixSize = 0;
static const int kEntrySize = 2;
static const int kEntrySize = entrysize;
};
// ObjectHashSet holds keys that are arbitrary objects by using the identity
// hash of the key for hashing purposes.
class ObjectHashSet: public HashTable<ObjectHashTableShape<1>, Object*> {
public:
static inline ObjectHashSet* cast(Object* obj) {
ASSERT(obj->IsHashTable());
return reinterpret_cast<ObjectHashSet*>(obj);
}
// Looks up whether the given key is part of this hash set.
bool Contains(Object* key);
// Adds the given key to this hash set.
MUST_USE_RESULT MaybeObject* Add(Object* key);
// Removes the given key from this hash set.
MUST_USE_RESULT MaybeObject* Remove(Object* key);
};
// ObjectHashTable maps keys that are JavaScript objects to object values by
// ObjectHashTable maps keys that are arbitrary objects to object values by
// using the identity hash of the key for hashing purposes.
class ObjectHashTable: public HashTable<ObjectHashTableShape, JSReceiver*> {
class ObjectHashTable: public HashTable<ObjectHashTableShape<2>, Object*> {
public:
static inline ObjectHashTable* cast(Object* obj) {
ASSERT(obj->IsHashTable());
......@@ -3003,16 +3040,16 @@ class ObjectHashTable: public HashTable<ObjectHashTableShape, JSReceiver*> {
// Looks up the value associated with the given key. The undefined value is
// returned in case the key is not present.
Object* Lookup(JSReceiver* key);
Object* Lookup(Object* key);
// Adds (or overwrites) the value associated with the given key. Mapping a
// key to the undefined value causes removal of the whole entry.
MUST_USE_RESULT MaybeObject* Put(JSReceiver* key, Object* value);
MUST_USE_RESULT MaybeObject* Put(Object* key, Object* value);
private:
friend class MarkCompactCollector;
void AddEntry(int entry, JSReceiver* key, Object* value);
void AddEntry(int entry, Object* key, Object* value);
void RemoveEntry(int entry, Heap* heap);
inline void RemoveEntry(int entry);
......@@ -7043,6 +7080,60 @@ class JSFunctionProxy: public JSProxy {
};
// The JSSet describes EcmaScript Harmony maps
class JSSet: public JSObject {
public:
// [set]: the backing hash set containing keys.
DECL_ACCESSORS(table, Object)
// Casting.
static inline JSSet* cast(Object* obj);
#ifdef OBJECT_PRINT
inline void JSSetPrint() {
JSSetPrint(stdout);
}
void JSSetPrint(FILE* out);
#endif
#ifdef DEBUG
void JSSetVerify();
#endif
static const int kTableOffset = JSObject::kHeaderSize;
static const int kSize = kTableOffset + kPointerSize;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSSet);
};
// The JSMap describes EcmaScript Harmony maps
class JSMap: public JSObject {
public:
// [table]: the backing hash table mapping keys to values.
DECL_ACCESSORS(table, Object)
// Casting.
static inline JSMap* cast(Object* obj);
#ifdef OBJECT_PRINT
inline void JSMapPrint() {
JSMapPrint(stdout);
}
void JSMapPrint(FILE* out);
#endif
#ifdef DEBUG
void JSMapVerify();
#endif
static const int kTableOffset = JSObject::kHeaderSize;
static const int kSize = kTableOffset + kPointerSize;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSMap);
};
// The JSWeakMap describes EcmaScript Harmony weak maps
class JSWeakMap: public JSObject {
public:
......
......@@ -717,6 +717,82 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Fix) {
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSSet, holder, 0);
Handle<ObjectHashSet> table = isolate->factory()->NewObjectHashSet(0);
holder->set_table(*table);
return *holder;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetAdd) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(JSSet, holder, 0);
Handle<Object> key(args[1]);
Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table()));
table = ObjectHashSetAdd(table, key);
holder->set_table(*table);
return isolate->heap()->undefined_symbol();
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetHas) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(JSSet, holder, 0);
Handle<Object> key(args[1]);
Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table()));
return isolate->heap()->ToBoolean(table->Contains(*key));
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetDelete) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(JSSet, holder, 0);
Handle<Object> key(args[1]);
Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table()));
table = ObjectHashSetRemove(table, key);
holder->set_table(*table);
return isolate->heap()->undefined_symbol();
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_MapInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSMap, holder, 0);
Handle<ObjectHashTable> table = isolate->factory()->NewObjectHashTable(0);
holder->set_table(*table);
return *holder;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_MapGet) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(JSMap, holder, 0);
Handle<Object> key(args[1]);
return ObjectHashTable::cast(holder->table())->Lookup(*key);
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_MapSet) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_CHECKED(JSMap, holder, 0);
Handle<Object> key(args[1]);
Handle<Object> value(args[2]);
Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
Handle<ObjectHashTable> new_table = PutIntoObjectHashTable(table, key, value);
holder->set_table(*new_table);
return *value;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
......
......@@ -296,6 +296,17 @@ namespace internal {
F(GetConstructTrap, 1, 1) \
F(Fix, 1, 1) \
\
/* Harmony sets */ \
F(SetInitialize, 1, 1) \
F(SetAdd, 2, 1) \
F(SetHas, 2, 1) \
F(SetDelete, 2, 1) \
\
/* Harmony maps */ \
F(MapInitialize, 1, 1) \
F(MapGet, 2, 1) \
F(MapSet, 3, 1) \
\
/* Harmony weakmaps */ \
F(WeakMapInitialize, 1, 1) \
F(WeakMapGet, 2, 1) \
......
......@@ -266,6 +266,18 @@ static inline uint32_t ComputeIntegerHash(uint32_t key) {
}
static inline uint32_t ComputeLongHash(uint64_t key) {
uint64_t hash = key;
hash = ~hash + (hash << 18); // hash = (hash << 18) - hash - 1;
hash = hash ^ (hash >> 31);
hash = hash * 21; // hash = (hash + (hash << 2)) + (hash << 4);
hash = hash ^ (hash >> 11);
hash = hash + (hash << 6);
hash = hash ^ (hash >> 22);
return (uint32_t) hash;
}
static inline uint32_t ComputePointerHash(void* ptr) {
return ComputeIntegerHash(
static_cast<uint32_t>(reinterpret_cast<intptr_t>(ptr)));
......
......@@ -63,7 +63,7 @@ bool V8::Initialize(Deserializer* des) {
FLAG_harmony_typeof = true;
FLAG_harmony_scoping = true;
FLAG_harmony_proxies = true;
FLAG_harmony_weakmaps = true;
FLAG_harmony_collections = true;
}
InitializeOncePerProcess();
......
......@@ -25,112 +25,218 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --harmony-weakmaps --expose-gc
// Flags: --harmony-collections --expose-gc
// Test valid getter and setter calls
var m = new WeakMap;
assertDoesNotThrow(function () { m.get(new Object) });
assertDoesNotThrow(function () { m.set(new Object) });
assertDoesNotThrow(function () { m.has(new Object) });
assertDoesNotThrow(function () { m.delete(new Object) });
// Test valid getter and setter calls on Sets.
function TestValidSetCalls(m) {
assertDoesNotThrow(function () { m.add(new Object) });
assertDoesNotThrow(function () { m.has(new Object) });
assertDoesNotThrow(function () { m.delete(new Object) });
}
TestValidSetCalls(new Set);
// Test invalid getter and setter calls
var m = new WeakMap;
assertThrows(function () { m.get(undefined) }, TypeError);
assertThrows(function () { m.set(undefined, 0) }, TypeError);
assertThrows(function () { m.get(0) }, TypeError);
assertThrows(function () { m.set(0, 0) }, TypeError);
assertThrows(function () { m.get('a-key') }, TypeError);
assertThrows(function () { m.set('a-key', 0) }, TypeError);
// Test valid getter and setter calls on Maps and WeakMaps
function TestValidMapCalls(m) {
assertDoesNotThrow(function () { m.get(new Object) });
assertDoesNotThrow(function () { m.set(new Object) });
assertDoesNotThrow(function () { m.has(new Object) });
assertDoesNotThrow(function () { m.delete(new Object) });
}
TestValidMapCalls(new Map);
TestValidMapCalls(new WeakMap);
// Test expected mapping behavior
var m = new WeakMap;
// Test invalid getter and setter calls for WeakMap only
function TestInvalidCalls(m) {
assertThrows(function () { m.get(undefined) }, TypeError);
assertThrows(function () { m.set(undefined, 0) }, TypeError);
assertThrows(function () { m.get(0) }, TypeError);
assertThrows(function () { m.set(0, 0) }, TypeError);
assertThrows(function () { m.get('a-key') }, TypeError);
assertThrows(function () { m.set('a-key', 0) }, TypeError);
}
TestInvalidCalls(new WeakMap);
// Test expected behavior for Sets
function TestSet(set, key) {
assertFalse(set.has(key));
set.add(key);
assertTrue(set.has(key));
set.delete(key);
assertFalse(set.has(key));
}
function TestSetBehavior(set) {
for (i = 0; i < 20; i++) {
TestSet(set, new Object);
}
}
TestSet(new Set, 23);
TestSet(new Set, 'foo');
TestSetBehavior(new Set);
// Test expected mapping behavior for Maps and WeakMaps
function TestMapping(map, key, value) {
map.set(key, value);
assertSame(value, map.get(key));
}
TestMapping(m, new Object, 23);
TestMapping(m, new Object, 'the-value');
TestMapping(m, new Object, new Object);
function TestMapBehavior1(m) {
TestMapping(m, new Object, 23);
TestMapping(m, new Object, 'the-value');
TestMapping(m, new Object, new Object);
}
TestMapBehavior1(new Map);
TestMapBehavior1(new WeakMap);
// Test expected querying behavior
var m = new WeakMap;
var key = new Object;
TestMapping(m, key, 'to-be-present');
assertTrue(m.has(key));
assertFalse(m.has(new Object));
TestMapping(m, key, undefined);
assertFalse(m.has(key));
assertFalse(m.has(new Object));
// Test expected mapping behavior for Maps only
function TestMapBehavior2(m) {
for (var i = 0; i < 20; i++) {
TestMapping(m, i, new Object);
TestMapping(m, i / 10, new Object);
TestMapping(m, 'key-' + i, new Object);
}
var keys = [ +0, -0, +Infinity, -Infinity, true, false ];
for (var i = 0; i < keys.length; i++) {
TestMapping(m, keys[i], new Object);
}
}
TestMapBehavior2(new Map);
// Test expected deletion behavior
var m = new WeakMap;
var key = new Object;
TestMapping(m, key, 'to-be-deleted');
assertTrue(m.delete(key));
assertFalse(m.delete(key));
assertFalse(m.delete(new Object));
assertSame(m.get(key), undefined);
// Test expected querying behavior of Maps and WeakMaps
function TestQuery(m) {
var key = new Object;
TestMapping(m, key, 'to-be-present');
assertTrue(m.has(key));
assertFalse(m.has(new Object));
TestMapping(m, key, undefined);
assertFalse(m.has(key));
assertFalse(m.has(new Object));
}
TestQuery(new Map);
TestQuery(new WeakMap);
// Test GC of map with entry
var m = new WeakMap;
var key = new Object;
m.set(key, 'not-collected');
gc();
assertSame('not-collected', m.get(key));
// Test expected deletion behavior of Maps and WeakMaps
function TestDelete(m) {
var key = new Object;
TestMapping(m, key, 'to-be-deleted');
assertTrue(m.delete(key));
assertFalse(m.delete(key));
assertFalse(m.delete(new Object));
assertSame(m.get(key), undefined);
}
TestDelete(new Map);
TestDelete(new WeakMap);
// Test GC of map with chained entries
var m = new WeakMap;
var head = new Object;
for (key = head, i = 0; i < 10; i++, key = m.get(key)) {
m.set(key, new Object);
// Test GC of Maps and WeakMaps with entry
function TestGC1(m) {
var key = new Object;
m.set(key, 'not-collected');
gc();
assertSame('not-collected', m.get(key));
}
gc();
var count = 0;
for (key = head; key != undefined; key = m.get(key)) {
count++;
TestGC1(new Map);
TestGC1(new WeakMap);
// Test GC of Maps and WeakMaps with chained entries
function TestGC2(m) {
var head = new Object;
for (key = head, i = 0; i < 10; i++, key = m.get(key)) {
m.set(key, new Object);
}
gc();
var count = 0;
for (key = head; key != undefined; key = m.get(key)) {
count++;
}
assertEquals(11, count);
}
assertEquals(11, count);
TestGC2(new Map);
TestGC2(new WeakMap);
// Test property attribute [[Enumerable]]
var m = new WeakMap;
function props(x) {
var array = [];
for (var p in x) array.push(p);
return array.sort();
function TestEnumerable(func) {
function props(x) {
var array = [];
for (var p in x) array.push(p);
return array.sort();
}
assertArrayEquals([], props(func));
assertArrayEquals([], props(func.prototype));
assertArrayEquals([], props(new func()));
}
assertArrayEquals([], props(WeakMap));
assertArrayEquals([], props(WeakMap.prototype));
assertArrayEquals([], props(m));
TestEnumerable(Set);
TestEnumerable(Map);
TestEnumerable(WeakMap);
// Test arbitrary properties on weak maps
var m = new WeakMap;
function TestProperty(map, property, value) {
map[property] = value;
assertEquals(value, map[property]);
// Test arbitrary properties on Maps and WeakMaps
function TestArbitrary(m) {
function TestProperty(map, property, value) {
map[property] = value;
assertEquals(value, map[property]);
}
for (i = 0; i < 20; i++) {
TestProperty(m, i, 'val' + i);
TestProperty(m, 'foo' + i, 'bar' + i);
}
TestMapping(m, new Object, 'foobar');
}
for (i = 0; i < 20; i++) {
TestProperty(m, i, 'val' + i);
TestProperty(m, 'foo' + i, 'bar' + i);
}
TestMapping(m, new Object, 'foobar');
TestArbitrary(new Map);
TestArbitrary(new WeakMap);
// Test direct constructor call
var m = WeakMap();
assertTrue(m instanceof WeakMap);
assertTrue(Set() instanceof Set);
assertTrue(Map() instanceof Map);
assertTrue(WeakMap() instanceof WeakMap);
// Test whether NaN values as keys are treated correctly.
var s = new Set;
assertFalse(s.has(NaN));
assertFalse(s.has(NaN + 1));
assertFalse(s.has(23));
s.add(NaN);
assertTrue(s.has(NaN));
assertTrue(s.has(NaN + 1));
assertFalse(s.has(23));
var m = new Map;
assertFalse(m.has(NaN));
assertFalse(m.has(NaN + 1));
assertFalse(m.has(23));
m.set(NaN, 'a-value');
assertTrue(m.has(NaN));
assertTrue(m.has(NaN + 1));
assertFalse(m.has(23));
// Test some common JavaScript idioms for Sets
var s = new Set;
assertTrue(s instanceof Set);
assertTrue(Set.prototype.add instanceof Function)
assertTrue(Set.prototype.has instanceof Function)
assertTrue(Set.prototype.delete instanceof Function)
// Test some common JavaScript idioms for Maps
var m = new Map;
assertTrue(m instanceof Map);
assertTrue(Map.prototype.set instanceof Function)
assertTrue(Map.prototype.get instanceof Function)
assertTrue(Map.prototype.has instanceof Function)
assertTrue(Map.prototype.delete instanceof Function)
// Test some common JavaScript idioms
// Test some common JavaScript idioms for WeakMaps
var m = new WeakMap;
assertTrue(m instanceof WeakMap);
assertTrue(WeakMap.prototype.set instanceof Function)
......@@ -164,4 +270,4 @@ assertEquals(10, o.myValue);
// Stress Test
// There is a proposed stress-test available at the es-discuss mailing list
// which cannot be reasonably automated. Check it out by hand if you like:
// https://mail.mozilla.org/pipermail/es-discuss/2011-May/014096.html
// https://mail.mozilla.org/pipermail/es-discuss/2011-May/014096.html
\ No newline at end of file
......@@ -25,42 +25,98 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --harmony-proxies --harmony-weakmaps
// Flags: --harmony-proxies --harmony-collections
// Helper.
function TestWithProxies(test, handler) {
test(handler, Proxy.create)
test(handler, function(h) {return Proxy.createFunction(h, function() {})})
function TestWithProxies(test, construct, handler) {
test(construct, handler, Proxy.create)
test(construct, handler, function(h) {
return Proxy.createFunction(h, function() {})
})
}
// Weak maps.
// Sets.
function TestWeakMap(fix) {
TestWithProxies(TestWeakMap2, fix)
function TestSet(construct, fix) {
TestWithProxies(TestSet2, construct, fix)
}
function TestWeakMap2(fix, create) {
function TestSet2(construct, fix, create) {
var handler = {fix: function() { return {} }}
var p1 = create(handler)
var p2 = create(handler)
var p3 = create(handler)
fix(p3)
var m = new WeakMap
var s = construct();
s.add(p1);
s.add(p2);
assertTrue(s.has(p1));
assertTrue(s.has(p2));
assertFalse(s.has(p3));
fix(p1)
fix(p2)
assertTrue(s.has(p1));
assertTrue(s.has(p2));
assertFalse(s.has(p3));
s.delete(p2);
assertTrue(s.has(p1));
assertFalse(s.has(p2));
assertFalse(s.has(p3));
}
TestSet(Set, Object.seal)
TestSet(Set, Object.freeze)
TestSet(Set, Object.preventExtensions)
// Maps and weak maps.
function TestMap(construct, fix) {
TestWithProxies(TestMap2, construct, fix)
}
function TestMap2(construct, fix, create) {
var handler = {fix: function() { return {} }}
var p1 = create(handler)
var p2 = create(handler)
var p3 = create(handler)
fix(p3)
var m = construct();
m.set(p1, 123);
m.set(p2, 321);
assertTrue(m.has(p1));
assertTrue(m.has(p2));
assertFalse(m.has(p3));
assertSame(123, m.get(p1));
assertSame(321, m.get(p2));
fix(p1)
fix(p2)
assertTrue(m.has(p1));
assertTrue(m.has(p2));
assertFalse(m.has(p3));
assertSame(123, m.get(p1));
assertSame(321, m.get(p2));
m.delete(p2);
assertTrue(m.has(p1));
assertFalse(m.has(p2));
assertFalse(m.has(p3));
assertSame(123, m.get(p1));
assertSame(undefined, m.get(p2));
}
TestWeakMap(Object.seal)
TestWeakMap(Object.freeze)
TestWeakMap(Object.preventExtensions)
TestMap(Map, Object.seal)
TestMap(Map, Object.freeze)
TestMap(Map, Object.preventExtensions)
TestMap(WeakMap, Object.seal)
TestMap(WeakMap, Object.freeze)
TestMap(WeakMap, Object.preventExtensions)
......@@ -709,7 +709,7 @@
'experimental_library_files': [
'../../src/macros.py',
'../../src/proxy.js',
'../../src/weakmap.js',
'../../src/collection.js',
],
},
'actions': [
......
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