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

Reply via email to