Revision: 8811
Author: [email protected]
Date: Wed Aug 3 04:55:13 2011
Log: Preliminary Harmony weak maps API implementation.
[email protected],[email protected]
BUG=v8:1565
TEST=mjsunit/harmony/weakmaps
Review URL: http://codereview.chromium.org/7529007
http://code.google.com/p/v8/source/detail?r=8811
Added:
/branches/bleeding_edge/src/weakmap.js
/branches/bleeding_edge/test/mjsunit/harmony/weakmaps.js
Modified:
/branches/bleeding_edge/src/SConscript
/branches/bleeding_edge/src/bootstrapper.cc
/branches/bleeding_edge/src/flag-definitions.h
/branches/bleeding_edge/src/messages.js
/branches/bleeding_edge/src/objects-debug.cc
/branches/bleeding_edge/src/objects-inl.h
/branches/bleeding_edge/src/objects-printer.cc
/branches/bleeding_edge/src/objects-visiting.cc
/branches/bleeding_edge/src/objects.cc
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/runtime.h
=======================================
--- /dev/null
+++ /branches/bleeding_edge/src/weakmap.js Wed Aug 3 04:55:13 2011
@@ -0,0 +1,72 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// 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 $WeakMap = global.WeakMap;
+
+// -------------------------------------------------------------------
+
+// Set the WeakMap function and constructor.
+%SetCode($WeakMap, function(x) {
+ if (%_IsConstructCall()) {
+ %WeakMapInitialize(this);
+ } else {
+ return new $WeakMap();
+ }
+});
+
+
+function WeakMapGet(key) {
+ if (!IS_SPEC_OBJECT(key)) {
+ throw %MakeTypeError('invalid_weakmap_key', [this, key]);
+ }
+ return %WeakMapGet(this, key);
+}
+
+
+function WeakMapSet(key, value) {
+ if (!IS_SPEC_OBJECT(key)) {
+ throw %MakeTypeError('invalid_weakmap_key', [this, key]);
+ }
+ return %WeakMapSet(this, key, value);
+}
+
+// -------------------------------------------------------------------
+
+function SetupWeakMap() {
+ // Setup the non-enumerable functions on the WeakMap prototype object.
+ InstallFunctionsOnHiddenPrototype($WeakMap.prototype, DONT_ENUM, $Array(
+ "get", WeakMapGet,
+ "set", WeakMapSet
+ ));
+}
+
+
+SetupWeakMap();
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/harmony/weakmaps.js Wed Aug 3
04:55:13 2011
@@ -0,0 +1,114 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (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
+
+
+// Test valid getter and setter calls
+var m = new WeakMap;
+assertDoesNotThrow(function () { m.get(new Object) });
+assertDoesNotThrow(function () { m.set(new Object) });
+
+
+// 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 expected mapping behavior
+var m = new WeakMap;
+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);
+
+
+// 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 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);
+}
+gc();
+var count = 0;
+for (key = head; key != undefined; key = m.get(key)) {
+ count++;
+}
+assertEquals(11, count);
+
+
+// Test property attribute [[Enumerable]]
+var m = new WeakMap;
+function props(x) {
+ var array = [];
+ for (var p in x) array.push(p);
+ return array.sort();
+}
+assertArrayEquals([], props(WeakMap));
+assertArrayEquals([], props(WeakMap.prototype));
+assertArrayEquals([], props(m));
+
+
+// Test arbitrary properties on weak maps
+var m = new WeakMap;
+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');
+
+
+// Test direct constructor call
+var m = WeakMap();
+assertTrue(m instanceof WeakMap);
+
+
+// Test some common JavaScript idioms
+var m = new WeakMap;
+assertTrue(m instanceof WeakMap);
+assertTrue(WeakMap.prototype.set instanceof Function)
+assertTrue(WeakMap.prototype.get instanceof Function)
=======================================
--- /branches/bleeding_edge/src/SConscript Wed Aug 3 04:12:46 2011
+++ /branches/bleeding_edge/src/SConscript Wed Aug 3 04:55:13 2011
@@ -308,6 +308,7 @@
EXPERIMENTAL_LIBRARY_FILES = '''
proxy.js
+weakmap.js
'''.split()
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Tue Jul 19 02:38:59 2011
+++ /branches/bleeding_edge/src/bootstrapper.cc Wed Aug 3 04:55:13 2011
@@ -199,6 +199,7 @@
// New context initialization. Used for creating a context from scratch.
void InitializeGlobal(Handle<GlobalObject> inner_global,
Handle<JSFunction> empty_function);
+ void InitializeExperimentalGlobal();
// Installs the contents of the native .js files on the global objects.
// Used for creating a context from scratch.
void InstallNativeFunctions();
@@ -1188,6 +1189,21 @@
// Initialize the data slot.
global_context()->set_data(heap->undefined_value());
}
+
+
+void Genesis::InitializeExperimentalGlobal() {
+ Isolate* isolate = this->isolate();
+ 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<JSFunction> weakmap_fun =
+ InstallFunction(global, "WeakMap", JS_WEAK_MAP_TYPE,
JSWeakMap::kSize,
+ isolate->initial_object_prototype(),
+ Builtins::kIllegal, true);
+ }
+}
bool Genesis::CompileBuiltin(Isolate* isolate, int index) {
@@ -1680,6 +1696,11 @@
"native proxy.js") == 0) {
if (!CompileExperimentalBuiltin(isolate(), i)) return false;
}
+ if (FLAG_harmony_weakmaps &&
+ strcmp(ExperimentalNatives::GetScriptName(i).start(),
+ "native weakmap.js") == 0) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
}
InstallExperimentalNativeFunctions();
@@ -2169,7 +2190,8 @@
isolate->counters()->contexts_created_from_scratch()->Increment();
}
- // Install experimental natives.
+ // Initialize experimental globals and install experimental natives.
+ InitializeExperimentalGlobal();
if (!InstallExperimentalNatives()) return;
result_ = global_context_;
=======================================
--- /branches/bleeding_edge/src/flag-definitions.h Thu Jul 28 03:17:41 2011
+++ /branches/bleeding_edge/src/flag-definitions.h Wed Aug 3 04:55:13 2011
@@ -98,6 +98,7 @@
// Flags for experimental language features.
DEFINE_bool(harmony_proxies, false, "enable harmony proxies")
+DEFINE_bool(harmony_weakmaps, false, "enable harmony weak maps")
// Flags for experimental implementation features.
DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of
doubles")
=======================================
--- /branches/bleeding_edge/src/messages.js Thu Jul 21 04:20:27 2011
+++ /branches/bleeding_edge/src/messages.js Wed Aug 3 04:55:13 2011
@@ -201,6 +201,7 @@
proxy_prop_not_configurable: ["Trap ", "%1", " of proxy
handler ", "%0", " returned non-configurable descriptor for
property ", "%2"],
proxy_non_object_prop_names: ["Trap ", "%1", " returned
non-object ", "%0"],
proxy_repeated_prop_name: ["Trap ", "%1", " returned repeated
property name ", "%2"],
+ invalid_weakmap_key: ["Invalid value used as weak map key"],
// RangeError
invalid_array_length: ["Invalid array length"],
stack_overflow: ["Maximum call stack size exceeded"],
=======================================
--- /branches/bleeding_edge/src/objects-debug.cc Wed Aug 3 04:12:46 2011
+++ /branches/bleeding_edge/src/objects-debug.cc Wed Aug 3 04:55:13 2011
@@ -153,6 +153,9 @@
case JS_ARRAY_TYPE:
JSArray::cast(this)->JSArrayVerify();
break;
+ case JS_WEAK_MAP_TYPE:
+ JSWeakMap::cast(this)->JSWeakMapVerify();
+ break;
case JS_REGEXP_TYPE:
JSRegExp::cast(this)->JSRegExpVerify();
break;
@@ -451,6 +454,14 @@
elements()->IsFixedArray() ||
elements()->IsFixedDoubleArray());
}
+
+
+void JSWeakMap::JSWeakMapVerify() {
+ CHECK(IsJSWeakMap());
+ JSObjectVerify();
+ VerifyHeapPointer(table());
+ ASSERT(table()->IsHashTable());
+}
void JSRegExp::JSRegExpVerify() {
=======================================
--- /branches/bleeding_edge/src/objects-inl.h Wed Aug 3 04:12:46 2011
+++ /branches/bleeding_edge/src/objects-inl.h Wed Aug 3 04:55:13 2011
@@ -479,6 +479,12 @@
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() {
@@ -1417,6 +1423,8 @@
return JSValue::kSize;
case JS_ARRAY_TYPE:
return JSValue::kSize;
+ case JS_WEAK_MAP_TYPE:
+ return JSWeakMap::kSize;
case JS_REGEXP_TYPE:
return JSValue::kSize;
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
@@ -2076,6 +2084,7 @@
CAST_ACCESSOR(JSRegExp)
CAST_ACCESSOR(JSProxy)
CAST_ACCESSOR(JSFunctionProxy)
+CAST_ACCESSOR(JSWeakMap)
CAST_ACCESSOR(Foreign)
CAST_ACCESSOR(ByteArray)
CAST_ACCESSOR(ExternalArray)
@@ -3851,6 +3860,9 @@
ACCESSORS(JSProxy, padding, Object, kPaddingOffset)
+ACCESSORS(JSWeakMap, table, ObjectHashTable, kTableOffset)
+
+
Address Foreign::address() {
return AddressFrom<Address>(READ_INTPTR_FIELD(this, kAddressOffset));
}
=======================================
--- /branches/bleeding_edge/src/objects-printer.cc Wed Aug 3 04:12:46 2011
+++ /branches/bleeding_edge/src/objects-printer.cc Wed Aug 3 04:55:13 2011
@@ -151,6 +151,9 @@
case JS_PROXY_TYPE:
JSProxy::cast(this)->JSProxyPrint(out);
break;
+ case JS_WEAK_MAP_TYPE:
+ JSWeakMap::cast(this)->JSWeakMapPrint(out);
+ break;
case FOREIGN_TYPE:
Foreign::cast(this)->ForeignPrint(out);
break;
@@ -431,6 +434,7 @@
case CODE_TYPE: return "CODE";
case JS_ARRAY_TYPE: return "JS_ARRAY";
case JS_PROXY_TYPE: return "JS_PROXY";
+ case JS_WEAK_MAP_TYPE: return "JS_WEAK_MAP";
case JS_REGEXP_TYPE: return "JS_REGEXP";
case JS_VALUE_TYPE: return "JS_VALUE";
case JS_GLOBAL_OBJECT_TYPE: return "JS_GLOBAL_OBJECT";
@@ -582,6 +586,16 @@
handler()->Print(out);
PrintF(out, "\n");
}
+
+
+void JSWeakMap::JSWeakMapPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSWeakMap");
+ PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - number of elements = %d\n", table()->NumberOfElements());
+ PrintF(out, " - table = ");
+ table()->ShortPrint(out);
+ PrintF(out, "\n");
+}
void JSFunction::JSFunctionPrint(FILE* out) {
=======================================
--- /branches/bleeding_edge/src/objects-visiting.cc Mon Jul 4 23:19:53 2011
+++ /branches/bleeding_edge/src/objects-visiting.cc Wed Aug 3 04:55:13 2011
@@ -115,6 +115,7 @@
case JS_GLOBAL_OBJECT_TYPE:
case JS_BUILTINS_OBJECT_TYPE:
case JS_MESSAGE_OBJECT_TYPE:
+ case JS_WEAK_MAP_TYPE:
return GetVisitorIdForSize(kVisitJSObject,
kVisitJSObjectGeneric,
instance_size);
=======================================
--- /branches/bleeding_edge/src/objects.cc Wed Aug 3 04:12:46 2011
+++ /branches/bleeding_edge/src/objects.cc Wed Aug 3 04:55:13 2011
@@ -963,6 +963,11 @@
accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
break;
}
+ case JS_WEAK_MAP_TYPE: {
+ int elements = JSWeakMap::cast(this)->table()->NumberOfElements();
+ accumulator->Add("<JS WeakMap[%d]>", elements);
+ break;
+ }
case JS_REGEXP_TYPE: {
accumulator->Add("<JS RegExp>");
break;
@@ -1193,6 +1198,7 @@
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_VALUE_TYPE:
case JS_ARRAY_TYPE:
+ case JS_WEAK_MAP_TYPE:
case JS_REGEXP_TYPE:
case JS_GLOBAL_PROXY_TYPE:
case JS_GLOBAL_OBJECT_TYPE:
=======================================
--- /branches/bleeding_edge/src/objects.h Wed Aug 3 04:12:46 2011
+++ /branches/bleeding_edge/src/objects.h Wed Aug 3 04:55:13 2011
@@ -51,6 +51,7 @@
// - JSReceiver (suitable for property access)
// - JSObject
// - JSArray
+// - JSWeakMap
// - JSRegExp
// - JSFunction
// - GlobalObject
@@ -333,6 +334,7 @@
V(JS_GLOBAL_PROXY_TYPE)
\
V(JS_ARRAY_TYPE)
\
V(JS_PROXY_TYPE)
\
+
V(JS_WEAK_MAP_TYPE)
\
V(JS_REGEXP_TYPE)
\
\
V(JS_FUNCTION_TYPE)
\
@@ -570,6 +572,7 @@
JS_GLOBAL_PROXY_TYPE,
JS_ARRAY_TYPE,
JS_PROXY_TYPE,
+ JS_WEAK_MAP_TYPE,
JS_REGEXP_TYPE, // LAST_NONCALLABLE_SPEC_OBJECT_TYPE
@@ -752,6 +755,7 @@
V(JSArray) \
V(JSProxy) \
V(JSFunctionProxy) \
+ V(JSWeakMap) \
V(JSRegExp) \
V(HashTable) \
V(Dictionary) \
@@ -6633,6 +6637,33 @@
};
+// The JSWeakMap describes EcmaScript Harmony weak maps
+class JSWeakMap: public JSObject {
+ public:
+ // [table]: the backing hash table mapping keys to values.
+ DECL_ACCESSORS(table, ObjectHashTable)
+
+ // Casting.
+ static inline JSWeakMap* cast(Object* obj);
+
+#ifdef OBJECT_PRINT
+ inline void JSWeakMapPrint() {
+ JSWeakMapPrint(stdout);
+ }
+ void JSWeakMapPrint(FILE* out);
+#endif
+#ifdef DEBUG
+ void JSWeakMapVerify();
+#endif
+
+ static const int kTableOffset = JSObject::kHeaderSize;
+ static const int kSize = kTableOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSWeakMap);
+};
+
+
// Foreign describes objects pointing from JavaScript to C structures.
// Since they cannot contain references to JS HeapObjects they can be
// placed in old_data_space.
=======================================
--- /branches/bleeding_edge/src/runtime.cc Wed Aug 3 04:12:46 2011
+++ /branches/bleeding_edge/src/runtime.cc Wed Aug 3 04:55:13 2011
@@ -633,6 +633,41 @@
proxy->Fix();
return isolate->heap()->undefined_value();
}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapInitialize) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSWeakMap, weakmap, 0);
+ ASSERT(weakmap->map()->inobject_properties() == 0);
+ Handle<ObjectHashTable> table =
isolate->factory()->NewObjectHashTable(0);
+ weakmap->set_table(*table);
+ return *weakmap;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapGet) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSWeakMap, weakmap, 0);
+ // TODO (mstarzinger): Currently we cannot use JSProxy objects as keys
+ // because they cannot be cast to JSObject to get an identity hash code.
+ CONVERT_ARG_CHECKED(JSObject, key, 1);
+ return weakmap->table()->Lookup(*key);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapSet) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_CHECKED(JSWeakMap, weakmap, 0);
+ // TODO (mstarzinger): See Runtime_WeakMapGet above.
+ CONVERT_ARG_CHECKED(JSObject, key, 1);
+ Handle<Object> value(args[2]);
+ Handle<ObjectHashTable> table(weakmap->table());
+ weakmap->set_table(*PutIntoObjectHashTable(table, key, value));
+ return *value;
+}
RUNTIME_FUNCTION(MaybeObject*, Runtime_ClassOf) {
=======================================
--- /branches/bleeding_edge/src/runtime.h Mon Jul 25 08:01:45 2011
+++ /branches/bleeding_edge/src/runtime.h Wed Aug 3 04:55:13 2011
@@ -287,6 +287,11 @@
F(GetHandler, 1, 1) \
F(Fix, 1, 1) \
\
+ /* Harmony weakmaps */ \
+ F(WeakMapInitialize, 1, 1) \
+ F(WeakMapGet, 2, 1) \
+ F(WeakMapSet, 3, 1) \
+ \
/* Statements */ \
F(NewClosure, 3, 1) \
F(NewObject, 1, 1) \
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev