Version 3.22.15

Enabled calling the SetReference* & SetObjectGroupId functions with a Persistent<SubclassOfValue>.

Performance and stability improvements on all platforms.

git-svn-id: http://v8.googlecode.com/svn/trunk@17266 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index 81a9775..29dcd40 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2013-10-18: Version 3.22.15
+
+        Enabled calling the SetReference* & SetObjectGroupId functions with a
+        Persistent<SubclassOfValue>.
+
+        Performance and stability improvements on all platforms.
+
+
 2013-10-17: Version 3.22.14
 
         Performance and stability improvements on all platforms.
diff --git a/include/v8.h b/include/v8.h
index 1ec0481..61aadc6 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -712,6 +712,7 @@
   V8_INLINE T* operator*() const { return val_; }
 
  private:
+  friend class Isolate;
   friend class Utils;
   template<class F> friend class Handle;
   template<class F> friend class Local;
@@ -4066,8 +4067,8 @@
    * garbage collection types it is sufficient to provide object groups
    * for partially dependent handles only.
    */
-  void SetObjectGroupId(const Persistent<Value>& object,
-                        UniqueId id);
+  template<typename T> void SetObjectGroupId(const Persistent<T>& object,
+                                             UniqueId id);
 
   /**
    * Allows the host application to declare implicit references from an object
@@ -4076,8 +4077,8 @@
    * are removed. It is intended to be used in the before-garbage-collection
    * callback function.
    */
-  void SetReferenceFromGroup(UniqueId id,
-                             const Persistent<Value>& child);
+  template<typename T> void SetReferenceFromGroup(UniqueId id,
+                                                  const Persistent<T>& child);
 
   /**
    * Allows the host application to declare implicit references from an object
@@ -4085,8 +4086,8 @@
    * too. After each garbage collection, all implicit references are removed. It
    * is intended to be used in the before-garbage-collection callback function.
    */
-  void SetReference(const Persistent<Object>& parent,
-                    const Persistent<Value>& child);
+  template<typename T, typename S>
+  void SetReference(const Persistent<T>& parent, const Persistent<S>& child);
 
   typedef void (*GCPrologueCallback)(Isolate* isolate,
                                      GCType type,
@@ -4140,8 +4141,11 @@
   Isolate& operator=(const Isolate&);
   void* operator new(size_t size);
   void operator delete(void*, size_t);
-};
 
+  void SetObjectGroupId(internal::Object** object, UniqueId id);
+  void SetReferenceFromGroup(UniqueId id, internal::Object** object);
+  void SetReference(internal::Object** parent, internal::Object** child);
+};
 
 class V8_EXPORT StartupData {
  public:
@@ -6420,6 +6424,33 @@
 }
 
 
+template<typename T>
+void Isolate::SetObjectGroupId(const Persistent<T>& object,
+                               UniqueId id) {
+  TYPE_CHECK(Value, T);
+  SetObjectGroupId(reinterpret_cast<v8::internal::Object**>(object.val_), id);
+}
+
+
+template<typename T>
+void Isolate::SetReferenceFromGroup(UniqueId id,
+                                    const Persistent<T>& object) {
+  TYPE_CHECK(Value, T);
+  SetReferenceFromGroup(id,
+                        reinterpret_cast<v8::internal::Object**>(object.val_));
+}
+
+
+template<typename T, typename S>
+void Isolate::SetReference(const Persistent<T>& parent,
+                           const Persistent<S>& child) {
+  TYPE_CHECK(Object, T);
+  TYPE_CHECK(Value, S);
+  SetReference(reinterpret_cast<v8::internal::Object**>(parent.val_),
+               reinterpret_cast<v8::internal::Object**>(child.val_));
+}
+
+
 Local<Value> Context::GetEmbedderData(int index) {
 #ifndef V8_ENABLE_CHECKS
   typedef internal::Object O;
diff --git a/src/api.cc b/src/api.cc
index 469c7a1..e498b43 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -6350,31 +6350,32 @@
 }
 
 
-void Isolate::SetObjectGroupId(const Persistent<Value>& object,
-                               UniqueId id) {
+void Isolate::SetObjectGroupId(internal::Object** object, UniqueId id) {
   i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(this);
   internal_isolate->global_handles()->SetObjectGroupId(
-      Utils::OpenPersistent(object).location(),
+      v8::internal::Handle<v8::internal::Object>(object).location(),
       id);
+  internal_isolate->global_handles()->SetObjectGroupId(object, id);
 }
 
 
-void Isolate::SetReferenceFromGroup(UniqueId id,
-                                    const Persistent<Value>& object) {
+void Isolate::SetReferenceFromGroup(UniqueId id, internal::Object** object) {
   i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(this);
   internal_isolate->global_handles()->SetReferenceFromGroup(
       id,
-      Utils::OpenPersistent(object).location());
+      v8::internal::Handle<v8::internal::Object>(object).location());
+  internal_isolate->global_handles()->SetReferenceFromGroup(id, object);
 }
 
 
-void Isolate::SetReference(const Persistent<Object>& parent,
-                           const Persistent<Value>& child) {
+void Isolate::SetReference(internal::Object** parent,
+                           internal::Object** child) {
   i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(this);
-  i::Object** parent_location = Utils::OpenPersistent(parent).location();
+  i::Object** parent_location =
+      v8::internal::Handle<v8::internal::Object>(parent).location();
   internal_isolate->global_handles()->SetReference(
       reinterpret_cast<i::HeapObject**>(parent_location),
-      Utils::OpenPersistent(child).location());
+      v8::internal::Handle<v8::internal::Object>(child).location());
 }
 
 
diff --git a/src/array-iterator.js b/src/array-iterator.js
index defd734..e734986 100644
--- a/src/array-iterator.js
+++ b/src/array-iterator.js
@@ -36,9 +36,9 @@
 var ARRAY_ITERATOR_KIND_ENTRIES = 3;
 // The spec draft also has "sparse" but it is never used.
 
-var iteratorObjectSymbol = %CreateSymbol(void 0);
-var arrayIteratorNextIndexSymbol = %CreateSymbol(void 0);
-var arrayIterationKindSymbol = %CreateSymbol(void 0);
+var iteratorObjectSymbol = %CreateSymbol(UNDEFINED);
+var arrayIteratorNextIndexSymbol = %CreateSymbol(UNDEFINED);
+var arrayIterationKindSymbol = %CreateSymbol(UNDEFINED);
 
 function ArrayIterator() {}
 
@@ -74,7 +74,7 @@
 
   if (index >= length) {
     iterator[arrayIteratorNextIndexSymbol] = 1 / 0; // Infinity
-    return CreateIteratorResultObject(void 0, true);
+    return CreateIteratorResultObject(UNDEFINED, true);
   }
 
   iterator[arrayIteratorNextIndexSymbol] = index + 1;
diff --git a/src/array.js b/src/array.js
index 4a7aea5..2649798 100644
--- a/src/array.js
+++ b/src/array.js
@@ -677,7 +677,7 @@
   var start_i = TO_INTEGER(start);
   var end_i = len;
 
-  if (end !== void 0) end_i = TO_INTEGER(end);
+  if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);
 
   if (start_i < 0) {
     start_i += len;
@@ -1016,7 +1016,7 @@
         var proto_length = indices;
         for (var i = from; i < proto_length; i++) {
           if (proto.hasOwnProperty(i)) {
-            obj[i] = void 0;
+            obj[i] = UNDEFINED;
           }
         }
       } else {
@@ -1024,7 +1024,7 @@
           var index = indices[i];
           if (!IS_UNDEFINED(index) && from <= index &&
               proto.hasOwnProperty(index)) {
-            obj[index] = void 0;
+            obj[index] = UNDEFINED;
           }
         }
       }
@@ -1061,7 +1061,7 @@
       if (first_undefined < last_defined) {
         // Fill in hole or undefined.
         obj[first_undefined] = obj[last_defined];
-        obj[last_defined] = void 0;
+        obj[last_defined] = UNDEFINED;
       }
     }
     // If there were any undefineds in the entire array, first_undefined
@@ -1073,12 +1073,12 @@
     // an undefined should be and vice versa.
     var i;
     for (i = first_undefined; i < length - num_holes; i++) {
-      obj[i] = void 0;
+      obj[i] = UNDEFINED;
     }
     for (i = length - num_holes; i < length; i++) {
       // For compatability with Webkit, do not expose elements in the prototype.
       if (i in %GetPrototype(obj)) {
-        obj[i] = void 0;
+        obj[i] = UNDEFINED;
       } else {
         delete obj[i];
       }
diff --git a/src/d8.js b/src/d8.js
index 3efea06..35b61d5 100644
--- a/src/d8.js
+++ b/src/d8.js
@@ -40,7 +40,7 @@
 
 function ToInspectableObject(obj) {
   if (!obj && typeof obj === 'object') {
-    return void 0;
+    return UNDEFINED;
   } else {
     return Object(obj);
   }
@@ -333,7 +333,7 @@
   }
 
   if ((cmd === undefined) || !cmd) {
-    this.request_ = void 0;
+    this.request_ = UNDEFINED;
     return;
   }
 
@@ -492,7 +492,7 @@
     case 'trace':
     case 'tr':
       // Return undefined to indicate command handled internally (no JSON).
-      this.request_ = void 0;
+      this.request_ = UNDEFINED;
       this.traceCommand_(args);
       break;
 
@@ -500,7 +500,7 @@
     case '?':
       this.helpCommand_(args);
       // Return undefined to indicate command handled internally (no JSON).
-      this.request_ = void 0;
+      this.request_ = UNDEFINED;
       break;
 
     default:
@@ -2124,7 +2124,7 @@
       var property_value_json;
       switch (typeof property_value) {
         case 'object':
-          if (property_value === null) {
+          if (IS_NULL(property_value)) {
             property_value_json = 'null';
           } else if (typeof property_value.toJSONProtocol == 'function') {
             property_value_json = property_value.toJSONProtocol(true);
@@ -2217,7 +2217,7 @@
     case "symbol":
       return "Symbol(" + (x.name ? Stringify(x.name, depth) : "") + ")"
     case "object":
-      if (x === null) return "null";
+      if (IS_NULL(x)) return "null";
       if (x.constructor && x.constructor.name === "Array") {
         var elems = [];
         for (var i = 0; i < x.length; ++i) {
@@ -2233,7 +2233,7 @@
       var props = [];
       for (var name in x) {
         var desc = Object.getOwnPropertyDescriptor(x, name);
-        if (desc === void 0) continue;
+        if (IS_UNDEFINED(desc)) continue;
         if ("value" in desc) {
           props.push(name + ": " + Stringify(desc.value, depth - 1));
         }
diff --git a/src/date.js b/src/date.js
index 62999e9..1b128c3 100644
--- a/src/date.js
+++ b/src/date.js
@@ -41,7 +41,7 @@
 }
 
 
-var timezone_cache_time = $NaN;
+var timezone_cache_time = NAN;
 var timezone_cache_timezone;
 
 function LocalTimezone(t) {
@@ -66,10 +66,10 @@
 
 // ECMA 262 - 15.9.1.11
 function MakeTime(hour, min, sec, ms) {
-  if (!$isFinite(hour)) return $NaN;
-  if (!$isFinite(min)) return $NaN;
-  if (!$isFinite(sec)) return $NaN;
-  if (!$isFinite(ms)) return $NaN;
+  if (!$isFinite(hour)) return NAN;
+  if (!$isFinite(min)) return NAN;
+  if (!$isFinite(sec)) return NAN;
+  if (!$isFinite(ms)) return NAN;
   return TO_INTEGER(hour) * msPerHour
       + TO_INTEGER(min) * msPerMinute
       + TO_INTEGER(sec) * msPerSecond
@@ -90,7 +90,7 @@
 //     MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1)
 //     MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11)
 function MakeDay(year, month, date) {
-  if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN;
+  if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return NAN;
 
   // Convert to integer and map -0 to 0.
   year = TO_INTEGER_MAP_MINUS_ZERO(year);
@@ -99,7 +99,7 @@
 
   if (year < kMinYear || year > kMaxYear ||
       month < kMinMonth || month > kMaxMonth) {
-    return $NaN;
+    return NAN;
   }
 
   // Now we rely on year and month being SMIs.
@@ -115,15 +115,15 @@
   // is no way that the time can be within range even after UTC
   // conversion we return NaN immediately instead of relying on
   // TimeClip to do it.
-  if ($abs(time) > MAX_TIME_BEFORE_UTC) return $NaN;
+  if ($abs(time) > MAX_TIME_BEFORE_UTC) return NAN;
   return time;
 }
 
 
 // ECMA 262 - 15.9.1.14
 function TimeClip(time) {
-  if (!$isFinite(time)) return $NaN;
-  if ($abs(time) > MAX_TIME_MS) return $NaN;
+  if (!$isFinite(time)) return NAN;
+  if ($abs(time) > MAX_TIME_MS) return NAN;
   return TO_INTEGER(time);
 }
 
@@ -132,7 +132,7 @@
 // strings over and over again.
 var Date_cache = {
   // Cached time value.
-  time: $NaN,
+  time: NAN,
   // String input for which the cached time is valid.
   string: null
 };
@@ -269,7 +269,7 @@
 // ECMA 262 - 15.9.4.2
 function DateParse(string) {
   var arr = %DateParseString(ToString(string), parse_buffer);
-  if (IS_NULL(arr)) return $NaN;
+  if (IS_NULL(arr)) return NAN;
 
   var day = MakeDay(arr[0], arr[1], arr[2]);
   var time = MakeTime(arr[3], arr[4], arr[5], arr[6]);
@@ -671,7 +671,7 @@
 function DateSetYear(year) {
   CHECK_DATE(this);
   year = ToNumber(year);
-  if (NUMBER_IS_NAN(year)) return SET_UTC_DATE_VALUE(this, $NaN);
+  if (NUMBER_IS_NAN(year)) return SET_UTC_DATE_VALUE(this, NAN);
   year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
       ? 1900 + TO_INTEGER(year) : year;
   var t = LOCAL_DATE_VALUE(this);
@@ -746,12 +746,12 @@
 
 function ResetDateCache() {
   // Reset the timezone cache:
-  timezone_cache_time = $NaN;
+  timezone_cache_time = NAN;
   timezone_cache_timezone = undefined;
 
   // Reset the date cache:
   cache = Date_cache;
-  cache.time = $NaN;
+  cache.time = NAN;
   cache.string = null;
 }
 
@@ -762,7 +762,7 @@
   %CheckIsBootstrapping();
 
   %SetCode($Date, DateConstructor);
-  %FunctionSetPrototype($Date, new $Date($NaN));
+  %FunctionSetPrototype($Date, new $Date(NAN));
 
   // Set up non-enumerable properties of the Date object itself.
   InstallFunctions($Date, DONT_ENUM, $Array(
diff --git a/src/debug-debugger.js b/src/debug-debugger.js
index 19209d4..b159ae3 100644
--- a/src/debug-debugger.js
+++ b/src/debug-debugger.js
@@ -448,7 +448,7 @@
 
   // If the position is not found in the script (the script might be shorter
   // than it used to be) just ignore it.
-  if (position === null) return;
+  if (IS_NULL(position)) return;
 
   // Create a break point object and set the break point.
   break_point = MakeBreakPoint(position, this);
@@ -2064,7 +2064,7 @@
   } else if ("value" in value_description) {
     return value_description.value;
   } else if (value_description.type == UNDEFINED_TYPE) {
-    return void 0;
+    return UNDEFINED;
   } else if (value_description.type == NULL_TYPE) {
     return null;
   } else {
diff --git a/src/i18n.js b/src/i18n.js
index aac440e..a64c7e6 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -290,7 +290,7 @@
  * Parameter locales is treated as a priority list.
  */
 function supportedLocalesOf(service, locales, options) {
-  if (service.match(GetServiceRE()) === null) {
+  if (IS_NULL(service.match(GetServiceRE()))) {
     throw new $Error('Internal error, wrong service type: ' + service);
   }
 
@@ -447,7 +447,7 @@
  * lookup algorithm.
  */
 function lookupMatcher(service, requestedLocales) {
-  if (service.match(GetServiceRE()) === null) {
+  if (IS_NULL(service.match(GetServiceRE()))) {
     throw new $Error('Internal error, wrong service type: ' + service);
   }
 
@@ -463,7 +463,7 @@
       if (AVAILABLE_LOCALES[service][locale] !== undefined) {
         // Return the resolved locale and extension.
         var extensionMatch = requestedLocales[i].match(GetUnicodeExtensionRE());
-        var extension = (extensionMatch === null) ? '' : extensionMatch[0];
+        var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0];
         return {'locale': locale, 'extension': extension, 'position': i};
       }
       // Truncate locale if possible.
@@ -535,7 +535,7 @@
  * Converts parameter to an Object if possible.
  */
 function toObject(value) {
-  if (value === undefined || value === null) {
+  if (IS_NULL_OR_UNDEFINED(value)) {
     throw new $TypeError('Value cannot be converted to an Object.');
   }
 
@@ -733,7 +733,7 @@
 function canonicalizeLanguageTag(localeID) {
   // null is typeof 'object' so we have to do extra check.
   if (typeof localeID !== 'string' && typeof localeID !== 'object' ||
-      localeID === null) {
+      IS_NULL(localeID)) {
     throw new $TypeError('Language ID should be string or object.');
   }
 
@@ -1449,7 +1449,7 @@
 
 
 function appendToDateTimeObject(options, option, match, pairs) {
-  if (match === null) {
+  if (IS_NULL(match)) {
     if (!options.hasOwnProperty(option)) {
       defineWEProperty(options, option, undefined);
     }
@@ -1751,7 +1751,7 @@
   // We expect only _ and / beside ASCII letters.
   // All inputs should conform to Area/Location from now on.
   var match = GetTimezoneNameCheckRE().exec(tzID);
-  if (match === null) {
+  if (IS_NULL(match)) {
     throw new $RangeError('Expected Area/Location for time zone, got ' + tzID);
   }
 
@@ -1971,7 +1971,7 @@
       throw new $TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
     }
 
-    if (this === undefined || this === null) {
+    if (IS_NULL_OR_UNDEFINED(this)) {
       throw new $TypeError('Method invoked on undefined or null value.');
     }
 
diff --git a/src/json.js b/src/json.js
index b0e14e1..c21e635 100644
--- a/src/json.js
+++ b/src/json.js
@@ -181,7 +181,7 @@
     }
   }
   // Undefined or a callable object.
-  return void 0;
+  return UNDEFINED;
 }
 
 
@@ -236,5 +236,5 @@
   var holder = {};
   holder[key] = object;
   // No need to pass the actual holder since there is no replacer function.
-  return JSONSerialize(key, holder, void 0, new InternalArray(), "", "");
+  return JSONSerialize(key, holder, UNDEFINED, new InternalArray(), "", "");
 }
diff --git a/src/liveedit-debugger.js b/src/liveedit-debugger.js
index a187b75..4618eda 100644
--- a/src/liveedit-debugger.js
+++ b/src/liveedit-debugger.js
@@ -186,7 +186,7 @@
     // to old version.
     if (link_to_old_script_list.length == 0) {
       %LiveEditReplaceScript(script, new_source, null);
-      old_script = void 0;
+      old_script = UNDEFINED;
     } else {
       var old_script_name = CreateNameForOldScript(script);
 
@@ -266,7 +266,7 @@
       // LiveEdit itself believe that any function in heap that points to a
       // particular script is a regular function.
       // For some functions we will restore this link later.
-      %LiveEditFunctionSetScript(info.shared_function_info, void 0);
+      %LiveEditFunctionSetScript(info.shared_function_info, UNDEFINED);
       compile_info.push(info);
       old_index_map.push(i);
     }
@@ -542,16 +542,16 @@
     this.children = children;
     // an index in array of compile_info
     this.array_index = array_index;
-    this.parent = void 0;
+    this.parent = UNDEFINED;
 
     this.status = FunctionStatus.UNCHANGED;
     // Status explanation is used for debugging purposes and will be shown
     // in user UI if some explanations are needed.
-    this.status_explanation = void 0;
-    this.new_start_pos = void 0;
-    this.new_end_pos = void 0;
-    this.corresponding_node = void 0;
-    this.unmatched_new_nodes = void 0;
+    this.status_explanation = UNDEFINED;
+    this.new_start_pos = UNDEFINED;
+    this.new_end_pos = UNDEFINED;
+    this.corresponding_node = UNDEFINED;
+    this.unmatched_new_nodes = UNDEFINED;
 
     // 'Textual' correspondence/matching is weaker than 'pure'
     // correspondence/matching. We need 'textual' level for visual presentation
@@ -559,10 +559,10 @@
     // Sometimes only function body is changed (functions in old and new script
     // textually correspond), but we cannot patch the code, so we see them
     // as an old function deleted and new function created.
-    this.textual_corresponding_node = void 0;
-    this.textually_unmatched_new_nodes = void 0;
+    this.textual_corresponding_node = UNDEFINED;
+    this.textually_unmatched_new_nodes = UNDEFINED;
 
-    this.live_shared_function_infos = void 0;
+    this.live_shared_function_infos = UNDEFINED;
   }
 
   // From array of function infos that is implicitly a tree creates
@@ -740,7 +740,7 @@
                 old_children[old_index].status_explanation =
                     "Enclosing function is now incompatible. " +
                     scope_change_description;
-                old_children[old_index].corresponding_node = void 0;
+                old_children[old_index].corresponding_node = UNDEFINED;
               } else if (old_children[old_index].status !=
                   FunctionStatus.UNCHANGED) {
                 ProcessNode(old_children[old_index],
@@ -748,7 +748,7 @@
                 if (old_children[old_index].status == FunctionStatus.DAMAGED) {
                   unmatched_new_nodes_list.push(
                       old_children[old_index].corresponding_node);
-                  old_children[old_index].corresponding_node = void 0;
+                  old_children[old_index].corresponding_node = UNDEFINED;
                   old_node.status = FunctionStatus.CHANGED;
                 }
               }
diff --git a/src/macros.py b/src/macros.py
index d699c14..1785d44 100644
--- a/src/macros.py
+++ b/src/macros.py
@@ -157,6 +157,11 @@
 macro TO_OBJECT_INLINE(arg) = (IS_SPEC_OBJECT(%IS_VAR(arg)) ? arg : ToObject(arg));
 macro JSON_NUMBER_TO_STRING(arg) = ((%_IsSmi(%IS_VAR(arg)) || arg - arg == 0) ? %_NumberToString(arg) : "null");
 
+# Constants.  The compiler constant folds them.
+const NAN = $NaN;
+const INFINITY = (1/0);
+const UNDEFINED = (void 0);
+
 # Macros implemented in Python.
 python macro CHAR_CODE(str) = ord(str[1]);
 
diff --git a/src/math.js b/src/math.js
index 9ba1934..47938a3 100644
--- a/src/math.js
+++ b/src/math.js
@@ -131,9 +131,9 @@
       return (arg1 == 0 && !%_IsSmi(arg1) && 1 / arg1 < 0) ? arg2 : arg1;
     }
     // All comparisons failed, one of the arguments must be NaN.
-    return 0/0;  // Compiler constant-folds this to NaN.
+    return NAN;
   }
-  var r = -1/0;  // Compiler constant-folds this to -Infinity.
+  var r = -INFINITY;
   for (var i = 0; i < length; i++) {
     var n = %_Arguments(i);
     if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
@@ -161,9 +161,9 @@
       return (arg1 == 0 && !%_IsSmi(arg1) && 1 / arg1 < 0) ? arg1 : arg2;
     }
     // All comparisons failed, one of the arguments must be NaN.
-    return 0/0;  // Compiler constant-folds this to NaN.
+    return NAN;
   }
-  var r = 1/0;  // Compiler constant-folds this to Infinity.
+  var r = INFINITY;
   for (var i = 0; i < length; i++) {
     var n = %_Arguments(i);
     if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
diff --git a/src/messages.js b/src/messages.js
index 2debbf8..0a30122 100644
--- a/src/messages.js
+++ b/src/messages.js
@@ -796,7 +796,7 @@
 }
 
 function CallSiteGetThis() {
-  return this[CallSiteStrictModeKey] ? void 0 : this[CallSiteReceiverKey];
+  return this[CallSiteStrictModeKey] ? UNDEFINED : this[CallSiteReceiverKey];
 }
 
 function CallSiteGetTypeName() {
@@ -826,7 +826,7 @@
 }
 
 function CallSiteGetFunction() {
-  return this[CallSiteStrictModeKey] ? void 0 : this[CallSiteFunctionKey];
+  return this[CallSiteStrictModeKey] ? UNDEFINED : this[CallSiteFunctionKey];
 }
 
 function CallSiteGetFunctionName() {
@@ -1092,7 +1092,7 @@
     var array = [];
     %MoveArrayContents(frames, array);
     formatting_custom_stack_trace = true;
-    var stack_trace = void 0;
+    var stack_trace = UNDEFINED;
     try {
       stack_trace = $Error.prepareStackTrace(obj, array);
     } catch (e) {
@@ -1160,7 +1160,7 @@
     // Turn this accessor into a data property.
     %DefineOrRedefineDataProperty(obj, 'stack', result, NONE);
     // Release context values.
-    stack = error_string = void 0;
+    stack = error_string = UNDEFINED;
     return result;
   };
 
@@ -1171,7 +1171,7 @@
     %DefineOrRedefineDataProperty(this, 'stack', v, NONE);
     if (this === obj) {
       // Release context values if holder is the same as the receiver.
-      stack = error_string = void 0;
+      stack = error_string = UNDEFINED;
     }
   };
 
@@ -1213,7 +1213,7 @@
         // Define all the expected properties directly on the error
         // object. This avoids going through getters and setters defined
         // on prototype objects.
-        %IgnoreAttributesAndSetProperty(this, 'stack', void 0, DONT_ENUM);
+        %IgnoreAttributesAndSetProperty(this, 'stack', UNDEFINED, DONT_ENUM);
         if (!IS_UNDEFINED(m)) {
           %IgnoreAttributesAndSetProperty(
             this, 'message', ToString(m), DONT_ENUM);
@@ -1251,7 +1251,7 @@
   while (error && !%HasLocalProperty(error, name)) {
     error = %GetPrototype(error);
   }
-  if (error === null) return void 0;
+  if (IS_NULL(error)) return UNDEFINED;
   if (!IS_OBJECT(error)) return error[name];
   // If the property is an accessor on one of the predefined errors that can be
   // generated statically by the compiler, don't touch it. This is to address
@@ -1260,11 +1260,11 @@
   if (desc && desc[IS_ACCESSOR_INDEX]) {
     var isName = name === "name";
     if (error === $ReferenceError.prototype)
-      return isName ? "ReferenceError" : void 0;
+      return isName ? "ReferenceError" : UNDEFINED;
     if (error === $SyntaxError.prototype)
-      return isName ? "SyntaxError" : void 0;
+      return isName ? "SyntaxError" : UNDEFINED;
     if (error === $TypeError.prototype)
-      return isName ? "TypeError" : void 0;
+      return isName ? "TypeError" : UNDEFINED;
   }
   // Otherwise, read normally.
   return error[name];
diff --git a/src/mirror-debugger.js b/src/mirror-debugger.js
index 3b360bb..4277136 100644
--- a/src/mirror-debugger.js
+++ b/src/mirror-debugger.js
@@ -117,7 +117,7 @@
  * @returns {Mirror} the mirror reflects the undefined value
  */
 function GetUndefinedMirror() {
-  return MakeMirror(void 0);
+  return MakeMirror(UNDEFINED);
 }
 
 
@@ -482,7 +482,7 @@
  * @extends ValueMirror
  */
 function UndefinedMirror() {
-  %_CallFunction(this, UNDEFINED_TYPE, void 0, ValueMirror);
+  %_CallFunction(this, UNDEFINED_TYPE, UNDEFINED, ValueMirror);
 }
 inherits(UndefinedMirror, ValueMirror);
 
@@ -957,7 +957,7 @@
 
 FunctionMirror.prototype.scope = function(index) {
   if (this.resolved()) {
-    return new ScopeMirror(void 0, this, index);
+    return new ScopeMirror(UNDEFINED, this, index);
   }
 };
 
@@ -1670,7 +1670,7 @@
 
 
 FrameMirror.prototype.scope = function(index) {
-  return new ScopeMirror(this, void 0, index);
+  return new ScopeMirror(this, UNDEFINED, index);
 };
 
 
diff --git a/src/mksnapshot.cc b/src/mksnapshot.cc
index 75babb5..95d3daa 100644
--- a/src/mksnapshot.cc
+++ b/src/mksnapshot.cc
@@ -43,49 +43,6 @@
 
 using namespace v8;
 
-static const unsigned int kMaxCounters = 256;
-
-// A single counter in a counter collection.
-class Counter {
- public:
-  static const int kMaxNameSize = 64;
-  int32_t* Bind(const char* name) {
-    int i;
-    for (i = 0; i < kMaxNameSize - 1 && name[i]; i++) {
-      name_[i] = name[i];
-    }
-    name_[i] = '\0';
-    return &counter_;
-  }
- private:
-  int32_t counter_;
-  uint8_t name_[kMaxNameSize];
-};
-
-
-// A set of counters and associated information.  An instance of this
-// class is stored directly in the memory-mapped counters file if
-// the --save-counters options is used
-class CounterCollection {
- public:
-  CounterCollection() {
-    magic_number_ = 0xDEADFACE;
-    max_counters_ = kMaxCounters;
-    max_name_size_ = Counter::kMaxNameSize;
-    counters_in_use_ = 0;
-  }
-  Counter* GetNextCounter() {
-    if (counters_in_use_ == kMaxCounters) return NULL;
-    return &counters_[counters_in_use_++];
-  }
- private:
-  uint32_t magic_number_;
-  uint32_t max_counters_;
-  uint32_t max_name_size_;
-  uint32_t counters_in_use_;
-  Counter counters_[kMaxCounters];
-};
-
 
 class Compressor {
  public:
diff --git a/src/object-observe.js b/src/object-observe.js
index b09c42d..9c7ac38 100644
--- a/src/object-observe.js
+++ b/src/object-observe.js
@@ -72,12 +72,12 @@
 ObservationWeakMap.prototype = {
   get: function(key) {
     key = %UnwrapGlobalProxy(key);
-    if (!IS_SPEC_OBJECT(key)) return void 0;
+    if (!IS_SPEC_OBJECT(key)) return UNDEFINED;
     return %WeakCollectionGet(this.map_, key);
   },
   set: function(key, value) {
     key = %UnwrapGlobalProxy(key);
-    if (!IS_SPEC_OBJECT(key)) return void 0;
+    if (!IS_SPEC_OBJECT(key)) return UNDEFINED;
     %WeakCollectionSet(this.map_, key, value);
   },
   has: function(key) {
@@ -492,7 +492,7 @@
 
   ObjectInfoAddPerformingType(objectInfo, changeType);
   try {
-    %_CallFunction(void 0, changeFn);
+    %_CallFunction(UNDEFINED, changeFn);
   } finally {
     ObjectInfoRemovePerformingType(objectInfo, changeType);
   }
@@ -525,7 +525,7 @@
   %MoveArrayContents(callbackInfo, delivered);
 
   try {
-    %_CallFunction(void 0, delivered, callback);
+    %_CallFunction(UNDEFINED, delivered, callback);
   } catch (ex) {}
   return true;
 }
diff --git a/src/objects.cc b/src/objects.cc
index 4aef808..2f84975 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -2137,7 +2137,9 @@
     AddSlowProperty(object, name, value, attributes);
   }
 
-  if (FLAG_harmony_observation && object->map()->is_observed()) {
+  if (FLAG_harmony_observation &&
+      object->map()->is_observed() &&
+      *name != isolate->heap()->hidden_string()) {
     Handle<Object> old_value = isolate->factory()->the_hole_value();
     EnqueueChangeRecord(object, "new", name, old_value);
   }
@@ -4015,8 +4017,10 @@
   }
 
   Handle<Object> old_value = isolate->factory()->the_hole_value();
-  if (FLAG_harmony_observation &&
-      object->map()->is_observed() && lookup->IsDataProperty()) {
+  bool is_observed = FLAG_harmony_observation &&
+                     object->map()->is_observed() &&
+                     *name != isolate->heap()->hidden_string();
+  if (is_observed && lookup->IsDataProperty()) {
     old_value = Object::GetProperty(object, name);
   }
 
@@ -4055,7 +4059,7 @@
 
   RETURN_IF_EMPTY_HANDLE_VALUE(isolate, result, Handle<Object>());
 
-  if (FLAG_harmony_observation && object->map()->is_observed()) {
+  if (is_observed) {
     if (lookup->IsTransition()) {
       EnqueueChangeRecord(object, "new", name, old_value);
     } else {
@@ -4156,7 +4160,9 @@
 
   Handle<Object> old_value = isolate->factory()->the_hole_value();
   PropertyAttributes old_attributes = ABSENT;
-  bool is_observed = FLAG_harmony_observation && object->map()->is_observed();
+  bool is_observed = FLAG_harmony_observation &&
+                     object->map()->is_observed() &&
+                     *name != isolate->heap()->hidden_string();
   if (is_observed && lookup.IsProperty()) {
     if (lookup.IsDataProperty()) old_value =
         Object::GetProperty(object, name);
@@ -5220,7 +5226,9 @@
   }
 
   Handle<Object> old_value = isolate->factory()->the_hole_value();
-  bool is_observed = FLAG_harmony_observation && object->map()->is_observed();
+  bool is_observed = FLAG_harmony_observation &&
+                     object->map()->is_observed() &&
+                     *name != isolate->heap()->hidden_string();
   if (is_observed && lookup.IsDataProperty()) {
     old_value = Object::GetProperty(object, name);
   }
@@ -6292,7 +6300,9 @@
   bool is_element = name->AsArrayIndex(&index);
 
   Handle<Object> old_value = isolate->factory()->the_hole_value();
-  bool is_observed = FLAG_harmony_observation && object->map()->is_observed();
+  bool is_observed = FLAG_harmony_observation &&
+                     object->map()->is_observed() &&
+                     *name != isolate->heap()->hidden_string();
   bool preexists = false;
   if (is_observed) {
     if (is_element) {
diff --git a/src/profile-generator-inl.h b/src/profile-generator-inl.h
index ac23ee3..e363f67 100644
--- a/src/profile-generator-inl.h
+++ b/src/profile-generator-inl.h
@@ -33,16 +33,6 @@
 namespace v8 {
 namespace internal {
 
-const char* StringsStorage::GetFunctionName(Name* name) {
-  return GetFunctionName(GetName(name));
-}
-
-
-const char* StringsStorage::GetFunctionName(const char* name) {
-  return strlen(name) > 0 ? name : ProfileGenerator::kAnonymousFunctionName;
-}
-
-
 CodeEntry::CodeEntry(Logger::LogEventsAndTags tag,
                      const char* name,
                      const char* name_prefix,
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index cf268af..acf54da 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -41,6 +41,12 @@
 namespace internal {
 
 
+bool StringsStorage::StringsMatch(void* key1, void* key2) {
+  return strcmp(reinterpret_cast<char*>(key1),
+                reinterpret_cast<char*>(key2)) == 0;
+}
+
+
 StringsStorage::StringsStorage(Heap* heap)
     : hash_seed_(heap->HashSeed()), names_(StringsMatch) {
 }
@@ -57,12 +63,15 @@
 
 const char* StringsStorage::GetCopy(const char* src) {
   int len = static_cast<int>(strlen(src));
-  Vector<char> dst = Vector<char>::New(len + 1);
-  OS::StrNCpy(dst, src, len);
-  dst[len] = '\0';
-  uint32_t hash =
-      StringHasher::HashSequentialString(dst.start(), len, hash_seed_);
-  return AddOrDisposeString(dst.start(), hash);
+  HashMap::Entry* entry = GetEntry(src, len);
+  if (entry->value == NULL) {
+    Vector<char> dst = Vector<char>::New(len + 1);
+    OS::StrNCpy(dst, src, len);
+    dst[len] = '\0';
+    entry->key = dst.start();
+    entry->value = entry->key;
+  }
+  return reinterpret_cast<const char*>(entry->value);
 }
 
 
@@ -75,15 +84,16 @@
 }
 
 
-const char* StringsStorage::AddOrDisposeString(char* str, uint32_t hash) {
-  HashMap::Entry* cache_entry = names_.Lookup(str, hash, true);
-  if (cache_entry->value == NULL) {
+const char* StringsStorage::AddOrDisposeString(char* str, int len) {
+  HashMap::Entry* entry = GetEntry(str, len);
+  if (entry->value == NULL) {
     // New entry added.
-    cache_entry->value = str;
+    entry->key = str;
+    entry->value = str;
   } else {
     DeleteArray(str);
   }
-  return reinterpret_cast<const char*>(cache_entry->value);
+  return reinterpret_cast<const char*>(entry->value);
 }
 
 
@@ -92,11 +102,9 @@
   int len = OS::VSNPrintF(str, format, args);
   if (len == -1) {
     DeleteArray(str.start());
-    return format;
+    return GetCopy(format);
   }
-  uint32_t hash = StringHasher::HashSequentialString(
-      str.start(), len, hash_seed_);
-  return AddOrDisposeString(str.start(), hash);
+  return AddOrDisposeString(str.start(), len);
 }
 
 
@@ -104,11 +112,11 @@
   if (name->IsString()) {
     String* str = String::cast(name);
     int length = Min(kMaxNameSize, str->length());
+    int actual_length = 0;
     SmartArrayPointer<char> data =
-        str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, 0, length);
-    uint32_t hash = StringHasher::HashSequentialString(
-        *data, length, name->GetHeap()->HashSeed());
-    return AddOrDisposeString(data.Detach(), hash);
+        str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, 0, length,
+                       &actual_length);
+    return AddOrDisposeString(data.Detach(), actual_length);
   } else if (name->IsSymbol()) {
     return "<symbol>";
   }
@@ -121,6 +129,21 @@
 }
 
 
+const char* StringsStorage::GetFunctionName(Name* name) {
+  return BeautifyFunctionName(GetName(name));
+}
+
+
+const char* StringsStorage::GetFunctionName(const char* name) {
+  return BeautifyFunctionName(GetCopy(name));
+}
+
+
+const char* StringsStorage::BeautifyFunctionName(const char* name) {
+  return (*name == 0) ? ProfileGenerator::kAnonymousFunctionName : name;
+}
+
+
 size_t StringsStorage::GetUsedMemorySize() const {
   size_t size = sizeof(*this);
   size += sizeof(HashMap::Entry) * names_.capacity();
@@ -131,6 +154,12 @@
 }
 
 
+HashMap::Entry* StringsStorage::GetEntry(const char* str, int len) {
+  uint32_t hash = StringHasher::HashSequentialString(str, len, hash_seed_);
+  return names_.Lookup(const_cast<char*>(str), hash, true);
+}
+
+
 const char* const CodeEntry::kEmptyNamePrefix = "";
 const char* const CodeEntry::kEmptyResourceName = "";
 const char* const CodeEntry::kEmptyBailoutReason = "";
diff --git a/src/profile-generator.h b/src/profile-generator.h
index 890fb2e..6e4758b 100644
--- a/src/profile-generator.h
+++ b/src/profile-generator.h
@@ -49,20 +49,18 @@
   const char* GetVFormatted(const char* format, va_list args);
   const char* GetName(Name* name);
   const char* GetName(int index);
-  inline const char* GetFunctionName(Name* name);
-  inline const char* GetFunctionName(const char* name);
+  const char* GetFunctionName(Name* name);
+  const char* GetFunctionName(const char* name);
   size_t GetUsedMemorySize() const;
 
  private:
   static const int kMaxNameSize = 1024;
 
-  static bool StringsMatch(void* key1, void* key2) {
-    return strcmp(reinterpret_cast<char*>(key1),
-                  reinterpret_cast<char*>(key2)) == 0;
-  }
-  const char* AddOrDisposeString(char* str, uint32_t hash);
+  static bool StringsMatch(void* key1, void* key2);
+  const char* BeautifyFunctionName(const char* name);
+  const char* AddOrDisposeString(char* str, int len);
+  HashMap::Entry* GetEntry(const char* str, int len);
 
-  // Mapping of strings by String::Hash to const char* strings.
   uint32_t hash_seed_;
   HashMap names_;
 
diff --git a/src/proxy.js b/src/proxy.js
index de9be50..4c03f21 100644
--- a/src/proxy.js
+++ b/src/proxy.js
@@ -40,7 +40,7 @@
     throw MakeTypeError("handler_non_object", ["create"])
   if (IS_UNDEFINED(proto))
     proto = null
-  else if (!(IS_SPEC_OBJECT(proto) || proto === null))
+  else if (!(IS_SPEC_OBJECT(proto) || IS_NULL(proto)))
     throw MakeTypeError("proto_non_object", ["create"])
   return %CreateJSProxy(handler, proto)
 }
@@ -56,7 +56,7 @@
     // Make sure the trap receives 'undefined' as this.
     var construct = constructTrap
     constructTrap = function() {
-      return %Apply(construct, void 0, arguments, 0, %_ArgumentsLength());
+      return %Apply(construct, UNDEFINED, arguments, 0, %_ArgumentsLength());
     }
   } else {
     throw MakeTypeError("trap_function_expected",
diff --git a/src/regexp.js b/src/regexp.js
index cb11ad1..22b0877 100644
--- a/src/regexp.js
+++ b/src/regexp.js
@@ -189,7 +189,7 @@
   // matchIndices is either null or the lastMatchInfo array.
   var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
 
-  if (matchIndices === null) {
+  if (IS_NULL(matchIndices)) {
     this.lastIndex = 0;
     return null;
   }
@@ -232,7 +232,7 @@
     %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
     // matchIndices is either null or the lastMatchInfo array.
     var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
-    if (matchIndices === null) {
+    if (IS_NULL(matchIndices)) {
       this.lastIndex = 0;
       return false;
     }
@@ -253,7 +253,7 @@
     %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [regexp, string, lastIndex]);
     // matchIndices is either null or the lastMatchInfo array.
     var matchIndices = %_RegExpExec(regexp, string, 0, lastMatchInfo);
-    if (matchIndices === null) {
+    if (IS_NULL(matchIndices)) {
       this.lastIndex = 0;
       return false;
     }
@@ -384,7 +384,7 @@
 var lastMatchInfo = new InternalPackedArray(
     2,                 // REGEXP_NUMBER_OF_CAPTURES
     "",                // Last subject.
-    void 0,            // Last input - settable with RegExpSetInput.
+    UNDEFINED,         // Last input - settable with RegExpSetInput.
     0,                 // REGEXP_FIRST_CAPTURE + 0
     0                  // REGEXP_FIRST_CAPTURE + 1
 );
diff --git a/src/runtime.js b/src/runtime.js
index 5339570..ce11c37 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -526,8 +526,8 @@
                                     : %StringToNumber(x);
   }
   if (IS_BOOLEAN(x)) return x ? 1 : 0;
-  if (IS_UNDEFINED(x)) return $NaN;
-  if (IS_SYMBOL(x)) return $NaN;
+  if (IS_UNDEFINED(x)) return NAN;
+  if (IS_SYMBOL(x)) return NAN;
   return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
 }
 
@@ -537,8 +537,8 @@
                                     : %StringToNumber(x);
   }
   if (IS_BOOLEAN(x)) return x ? 1 : 0;
-  if (IS_UNDEFINED(x)) return $NaN;
-  if (IS_SYMBOL(x)) return $NaN;
+  if (IS_UNDEFINED(x)) return NAN;
+  if (IS_SYMBOL(x)) return NAN;
   return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
 }
 
diff --git a/src/string.js b/src/string.js
index cb82c16..14b44ca 100644
--- a/src/string.js
+++ b/src/string.js
@@ -28,7 +28,6 @@
 // This file relies on the fact that the following declaration has been made
 // in runtime.js:
 // var $String = global.String;
-// var $NaN = 0/0;
 
 // -------------------------------------------------------------------
 
@@ -574,7 +573,7 @@
   var s_len = s.length;
   var start_i = TO_INTEGER(start);
   var end_i = s_len;
-  if (end !== void 0) {
+  if (!IS_UNDEFINED(end)) {
     end_i = TO_INTEGER(end);
   }
 
@@ -699,7 +698,7 @@
         %_CallFunction(result, %_SubString(subject, start, end),
                        ArrayPushBuiltin);
       } else {
-        %_CallFunction(result, void 0, ArrayPushBuiltin);
+        %_CallFunction(result, UNDEFINED, ArrayPushBuiltin);
       }
       if (result.length === limit) break outer_loop;
     }
@@ -756,7 +755,7 @@
 
   // Correct n: If not given, set to string length; if explicitly
   // set to undefined, zero, or negative, returns empty string.
-  if (n === void 0) {
+  if (IS_UNDEFINED(n)) {
     len = s.length;
   } else {
     len = TO_INTEGER(n);
@@ -765,7 +764,7 @@
 
   // Correct start: If not given (or undefined), set to zero; otherwise
   // convert to integer and handle negative case.
-  if (start === void 0) {
+  if (IS_UNDEFINED(start)) {
     start = 0;
   } else {
     start = TO_INTEGER(start);
diff --git a/src/v8natives.js b/src/v8natives.js
index 76eeac6..c42d5c4 100644
--- a/src/v8natives.js
+++ b/src/v8natives.js
@@ -32,7 +32,6 @@
 // var $Number = global.Number;
 // var $Function = global.Function;
 // var $Array = global.Array;
-// var $NaN = 0/0;
 //
 // in math.js:
 // var $floor = MathFloor
@@ -95,7 +94,7 @@
   }
   if (fields) {
     for (var i = 0; i < fields.length; i++) {
-      %SetProperty(prototype, fields[i], void 0, DONT_ENUM | DONT_DELETE);
+      %SetProperty(prototype, fields[i], UNDEFINED, DONT_ENUM | DONT_DELETE);
     }
   }
   for (var i = 0; i < methods.length; i += 2) {
@@ -148,7 +147,7 @@
     string = TO_STRING_INLINE(string);
     radix = TO_INT32(radix);
     if (!(radix == 0 || (2 <= radix && radix <= 36))) {
-      return $NaN;
+      return NAN;
     }
   }
 
@@ -197,15 +196,16 @@
 function SetUpGlobal() {
   %CheckIsBootstrapping();
 
+  var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY;
+
   // ECMA 262 - 15.1.1.1.
-  %SetProperty(global, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY);
+  %SetProperty(global, "NaN", NAN, attributes);
 
   // ECMA-262 - 15.1.1.2.
-  %SetProperty(global, "Infinity", 1/0, DONT_ENUM | DONT_DELETE | READ_ONLY);
+  %SetProperty(global, "Infinity", INFINITY, attributes);
 
   // ECMA-262 - 15.1.1.3.
-  %SetProperty(global, "undefined", void 0,
-               DONT_ENUM | DONT_DELETE | READ_ONLY);
+  %SetProperty(global, "undefined", UNDEFINED, attributes);
 
   // Set up non-enumerable function on the global object.
   InstallFunctions(global, DONT_ENUM, $Array(
@@ -475,12 +475,12 @@
 function ToCompletePropertyDescriptor(obj) {
   var desc = ToPropertyDescriptor(obj);
   if (IsGenericDescriptor(desc) || IsDataDescriptor(desc)) {
-    if (!desc.hasValue()) desc.setValue(void 0);
+    if (!desc.hasValue()) desc.setValue(UNDEFINED);
     if (!desc.hasWritable()) desc.setWritable(false);
   } else {
     // Is accessor descriptor.
-    if (!desc.hasGetter()) desc.setGet(void 0);
-    if (!desc.hasSetter()) desc.setSet(void 0);
+    if (!desc.hasGetter()) desc.setGet(UNDEFINED);
+    if (!desc.hasSetter()) desc.setSet(UNDEFINED);
   }
   if (!desc.hasEnumerable()) desc.setEnumerable(false);
   if (!desc.hasConfigurable()) desc.setConfigurable(false);
@@ -491,7 +491,7 @@
 function PropertyDescriptor() {
   // Initialize here so they are all in-object and have the same map.
   // Default values from ES5 8.6.1.
-  this.value_ = void 0;
+  this.value_ = UNDEFINED;
   this.hasValue_ = false;
   this.writable_ = false;
   this.hasWritable_ = false;
@@ -499,9 +499,9 @@
   this.hasEnumerable_ = false;
   this.configurable_ = false;
   this.hasConfigurable_ = false;
-  this.get_ = void 0;
+  this.get_ = UNDEFINED;
   this.hasGetter_ = false;
-  this.set_ = void 0;
+  this.set_ = UNDEFINED;
   this.hasSetter_ = false;
 }
 
@@ -593,7 +593,7 @@
   }
 
   if (IS_UNDEFINED(desc_array)) {
-    return void 0;
+    return UNDEFINED;
   }
 
   var desc = new PropertyDescriptor();
@@ -647,10 +647,11 @@
   var p = ToName(v);
   if (%IsJSProxy(obj)) {
     // TODO(rossberg): adjust once there is a story for symbols vs proxies.
-    if (IS_SYMBOL(v)) return void 0;
+    if (IS_SYMBOL(v)) return UNDEFINED;
 
     var handler = %GetHandler(obj);
-    var descriptor = CallTrap1(handler, "getOwnPropertyDescriptor", void 0, p);
+    var descriptor = CallTrap1(
+                         handler, "getOwnPropertyDescriptor", UNDEFINED, p);
     if (IS_UNDEFINED(descriptor)) return descriptor;
     var desc = ToCompletePropertyDescriptor(descriptor);
     if (!desc.isConfigurable()) {
@@ -666,7 +667,7 @@
   var props = %GetOwnProperty(ToObject(obj), p);
 
   // A false value here means that access checks failed.
-  if (props === false) return void 0;
+  if (props === false) return UNDEFINED;
 
   return ConvertDescriptorArrayToDescriptor(props);
 }
@@ -693,7 +694,7 @@
   if (IS_SYMBOL(p)) return false;
 
   var handler = %GetHandler(obj);
-  var result = CallTrap2(handler, "defineProperty", void 0, p, attributes);
+  var result = CallTrap2(handler, "defineProperty", UNDEFINED, p, attributes);
   if (!ToBoolean(result)) {
     if (should_throw) {
       throw MakeTypeError("handler_returned_false",
@@ -710,7 +711,7 @@
 function DefineObjectProperty(obj, p, desc, should_throw) {
   var current_or_access = %GetOwnProperty(ToObject(obj), ToName(p));
   // A false value here means that access checks failed.
-  if (current_or_access === false) return void 0;
+  if (current_or_access === false) return UNDEFINED;
 
   var current = ConvertDescriptorArrayToDescriptor(current_or_access);
   var extensible = %IsExtensible(ToObject(obj));
@@ -841,7 +842,7 @@
       flag |= READ_ONLY;
     }
 
-    var value = void 0;  // Default value is undefined.
+    var value = UNDEFINED;  // Default value is undefined.
     if (desc.hasValue()) {
       value = desc.getValue();
     } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) {
@@ -920,7 +921,7 @@
     // For the time being, we need a hack to prevent Object.observe from
     // generating two change records.
     obj.length = new_length;
-    desc.value_ = void 0;
+    desc.value_ = UNDEFINED;
     desc.hasValue_ = false;
     threw = !DefineObjectProperty(obj, "length", desc, should_throw) || threw;
     if (emit_splice) {
@@ -1045,7 +1046,7 @@
   // Special handling for proxies.
   if (%IsJSProxy(obj)) {
     var handler = %GetHandler(obj);
-    var names = CallTrap0(handler, "getOwnPropertyNames", void 0);
+    var names = CallTrap0(handler, "getOwnPropertyNames", UNDEFINED);
     return ToNameArray(names, "getOwnPropertyNames", false);
   }
 
@@ -1194,7 +1195,7 @@
 // Harmony proxies.
 function ProxyFix(obj) {
   var handler = %GetHandler(obj);
-  var props = CallTrap0(handler, "fix", void 0);
+  var props = CallTrap0(handler, "fix", UNDEFINED);
   if (IS_UNDEFINED(props)) {
     throw MakeTypeError("handler_returned_undefined", [handler, "fix"]);
   }
@@ -1560,8 +1561,8 @@
   }
 
   if (NUMBER_IS_NAN(x)) return "NaN";
-  if (x == 1/0) return "Infinity";
-  if (x == -1/0) return "-Infinity";
+  if (x == INFINITY) return "Infinity";
+  if (x == -INFINITY) return "-Infinity";
 
   return %NumberToFixed(x, f);
 }
@@ -1578,11 +1579,11 @@
     // Get the value of this number in case it's an object.
     x = %_ValueOf(this);
   }
-  var f = IS_UNDEFINED(fractionDigits) ? void 0 : TO_INTEGER(fractionDigits);
+  var f = IS_UNDEFINED(fractionDigits) ? UNDEFINED : TO_INTEGER(fractionDigits);
 
   if (NUMBER_IS_NAN(x)) return "NaN";
-  if (x == 1/0) return "Infinity";
-  if (x == -1/0) return "-Infinity";
+  if (x == INFINITY) return "Infinity";
+  if (x == -INFINITY) return "-Infinity";
 
   if (IS_UNDEFINED(f)) {
     f = -1;  // Signal for runtime function that f is not defined.
@@ -1608,8 +1609,8 @@
   var p = TO_INTEGER(precision);
 
   if (NUMBER_IS_NAN(x)) return "NaN";
-  if (x == 1/0) return "Infinity";
-  if (x == -1/0) return "-Infinity";
+  if (x == INFINITY) return "Infinity";
+  if (x == -INFINITY) return "-Infinity";
 
   if (p < 1 || p > 21) {
     throw new $RangeError("toPrecision() argument must be between 1 and 21");
@@ -1654,18 +1655,18 @@
                DONT_ENUM | DONT_DELETE | READ_ONLY);
 
   // ECMA-262 section 15.7.3.3.
-  %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY);
+  %SetProperty($Number, "NaN", NAN, DONT_ENUM | DONT_DELETE | READ_ONLY);
 
   // ECMA-262 section 15.7.3.4.
   %SetProperty($Number,
                "NEGATIVE_INFINITY",
-               -1/0,
+               -INFINITY,
                DONT_ENUM | DONT_DELETE | READ_ONLY);
 
   // ECMA-262 section 15.7.3.5.
   %SetProperty($Number,
                "POSITIVE_INFINITY",
-               1/0,
+               INFINITY,
                DONT_ENUM | DONT_DELETE | READ_ONLY);
   %ToFastProperties($Number);
 
diff --git a/src/version.cc b/src/version.cc
index aac2a8c..42efb3b 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
 // system so their names cannot be changed without changing the scripts.
 #define MAJOR_VERSION     3
 #define MINOR_VERSION     22
-#define BUILD_NUMBER      14
+#define BUILD_NUMBER      15
 #define PATCH_LEVEL       0
 // Use 1 for candidates and 0 otherwise.
 // (Boolean macro values are not supported by all preprocessors.)
diff --git a/test/benchmarks/testcfg.py b/test/benchmarks/testcfg.py
index 5fb3f51..b15553a 100644
--- a/test/benchmarks/testcfg.py
+++ b/test/benchmarks/testcfg.py
@@ -172,7 +172,7 @@
 
     os.chdir(old_cwd)
 
-  def VariantFlags(self):
+  def VariantFlags(self, testcase, default_flags):
     # Both --nocrankshaft and --stressopt are very slow.
     return [[]]
 
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index c70f988..6e955bd 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -3318,8 +3318,9 @@
 };
 
 
+template<typename T>
 static void WeakPointerCallback(v8::Isolate* isolate,
-                                Persistent<Value>* handle,
+                                Persistent<T>* handle,
                                 WeakCallCounter* counter) {
   CHECK_EQ(1234, counter->id());
   counter->increment();
@@ -3327,7 +3328,8 @@
 }
 
 
-static UniqueId MakeUniqueId(const Persistent<Value>& p) {
+template<typename T>
+static UniqueId MakeUniqueId(const Persistent<T>& p) {
   return UniqueId(reinterpret_cast<uintptr_t>(*v8::Utils::OpenPersistent(p)));
 }
 
@@ -3425,6 +3427,97 @@
 }
 
 
+THREADED_TEST(ApiObjectGroupsForSubtypes) {
+  LocalContext env;
+  v8::Isolate* iso = env->GetIsolate();
+  HandleScope scope(iso);
+
+  Persistent<Object> g1s1;
+  Persistent<String> g1s2;
+  Persistent<String> g1c1;
+  Persistent<Object> g2s1;
+  Persistent<String> g2s2;
+  Persistent<String> g2c1;
+
+  WeakCallCounter counter(1234);
+
+  {
+    HandleScope scope(iso);
+    g1s1.Reset(iso, Object::New());
+    g1s2.Reset(iso, String::New("foo1"));
+    g1c1.Reset(iso, String::New("foo2"));
+    g1s1.MakeWeak(&counter, &WeakPointerCallback);
+    g1s2.MakeWeak(&counter, &WeakPointerCallback);
+    g1c1.MakeWeak(&counter, &WeakPointerCallback);
+
+    g2s1.Reset(iso, Object::New());
+    g2s2.Reset(iso, String::New("foo3"));
+    g2c1.Reset(iso, String::New("foo4"));
+    g2s1.MakeWeak(&counter, &WeakPointerCallback);
+    g2s2.MakeWeak(&counter, &WeakPointerCallback);
+    g2c1.MakeWeak(&counter, &WeakPointerCallback);
+  }
+
+  Persistent<Value> root(iso, g1s1);  // make a root.
+
+  // Connect group 1 and 2, make a cycle.
+  {
+    HandleScope scope(iso);
+    CHECK(Local<Object>::New(iso, g1s1)->Set(0, Local<Object>::New(iso, g2s1)));
+    CHECK(Local<Object>::New(iso, g2s1)->Set(0, Local<Object>::New(iso, g1s1)));
+  }
+
+  {
+    UniqueId id1 = MakeUniqueId(g1s1);
+    UniqueId id2 = MakeUniqueId(g2s2);
+    iso->SetObjectGroupId(g1s1, id1);
+    iso->SetObjectGroupId(g1s2, id1);
+    iso->SetReference(g1s1, g1c1);
+    iso->SetObjectGroupId(g2s1, id2);
+    iso->SetObjectGroupId(g2s2, id2);
+    iso->SetReferenceFromGroup(id2, g2c1);
+  }
+  // Do a single full GC, ensure incremental marking is stopped.
+  v8::internal::Heap* heap = reinterpret_cast<v8::internal::Isolate*>(
+      iso)->heap();
+  heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
+
+  // All object should be alive.
+  CHECK_EQ(0, counter.NumberOfWeakCalls());
+
+  // Weaken the root.
+  root.MakeWeak(&counter, &WeakPointerCallback);
+  // But make children strong roots---all the objects (except for children)
+  // should be collectable now.
+  g1c1.ClearWeak();
+  g2c1.ClearWeak();
+
+  // Groups are deleted, rebuild groups.
+  {
+    UniqueId id1 = MakeUniqueId(g1s1);
+    UniqueId id2 = MakeUniqueId(g2s2);
+    iso->SetObjectGroupId(g1s1, id1);
+    iso->SetObjectGroupId(g1s2, id1);
+    iso->SetReference(g1s1, g1c1);
+    iso->SetObjectGroupId(g2s1, id2);
+    iso->SetObjectGroupId(g2s2, id2);
+    iso->SetReferenceFromGroup(id2, g2c1);
+  }
+
+  heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
+
+  // All objects should be gone. 5 global handles in total.
+  CHECK_EQ(5, counter.NumberOfWeakCalls());
+
+  // And now make children weak again and collect them.
+  g1c1.MakeWeak(&counter, &WeakPointerCallback);
+  g2c1.MakeWeak(&counter, &WeakPointerCallback);
+
+  heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
+  CHECK_EQ(7, counter.NumberOfWeakCalls());
+}
+
+
 THREADED_TEST(ApiObjectGroupsCycle) {
   LocalContext env;
   v8::Isolate* iso = env->GetIsolate();
diff --git a/test/cctest/test-object-observe.cc b/test/cctest/test-object-observe.cc
index a9f840e..b4488a6 100644
--- a/test/cctest/test-object-observe.cc
+++ b/test/cctest/test-object-observe.cc
@@ -720,3 +720,18 @@
   }
   CHECK(CompileRun("records")->IsNull());
 }
+
+
+TEST(HiddenPropertiesLeakage) {
+  HarmonyIsolate isolate;
+  HandleScope scope(isolate.GetIsolate());
+  LocalContext context(isolate.GetIsolate());
+  CompileRun("var obj = {};"
+             "var records = null;"
+             "var observer = function(r) { records = r };"
+             "Object.observe(obj, observer);");
+  Handle<Value> obj = context->Global()->Get(String::New("obj"));
+  Handle<Object>::Cast(obj)->SetHiddenValue(String::New("foo"), Null());
+  CompileRun("");  // trigger delivery
+  CHECK(CompileRun("records")->IsNull());
+}
diff --git a/test/mjsunit/harmony/object-observe.js b/test/mjsunit/harmony/object-observe.js
index f982a66..f94ab75 100644
--- a/test/mjsunit/harmony/object-observe.js
+++ b/test/mjsunit/harmony/object-observe.js
@@ -286,6 +286,20 @@
   { object: obj, type: 'new', name: 'id' },
 ]);
 
+// The empty-string property is observable
+reset();
+var obj = {};
+Object.observe(obj, observer.callback);
+obj[''] = '';
+obj[''] = ' ';
+delete obj[''];
+Object.deliverChangeRecords(observer.callback);
+observer.assertCallbackRecords([
+  { object: obj, type: 'new', name: '' },
+  { object: obj, type: 'updated', name: '', oldValue: '' },
+  { object: obj, type: 'deleted', name: '', oldValue: ' ' },
+]);
+
 // Observing a continuous stream of changes, while itermittantly unobserving.
 reset();
 Object.observe(obj, observer.callback);
diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status
index ec780e5..9fbb9c0 100644
--- a/test/mjsunit/mjsunit.status
+++ b/test/mjsunit/mjsunit.status
@@ -66,7 +66,7 @@
   'array-constructor': [PASS, TIMEOUT],
 
   # Very slow on ARM and MIPS, contains no architecture dependent code.
-  'unicode-case-overoptimization': [PASS, ['arch == arm or arch == android_arm or arch == mipsel', TIMEOUT]],
+  'unicode-case-overoptimization': [PASS, NO_VARIANTS, ['arch == arm or arch == android_arm or arch == mipsel', TIMEOUT]],
 
   ##############################################################################
   # This test expects to reach a certain recursion depth, which may not work
diff --git a/test/mjsunit/regress/regress-crbug-306851.js b/test/mjsunit/regress/regress-crbug-306851.js
new file mode 100644
index 0000000..77b711a
--- /dev/null
+++ b/test/mjsunit/regress/regress-crbug-306851.js
@@ -0,0 +1,52 @@
+// Copyright 2013 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: --allow-natives-syntax
+
+function Counter() {
+  this.value = 0;
+};
+
+Object.defineProperty(Counter.prototype, 'count', {
+  get: function() { return this.value; },
+  set: function(value) { this.value = value; }
+});
+
+var obj = new Counter();
+
+function bummer() {
+  obj.count++;
+  return obj.count;
+}
+
+assertEquals(1, bummer());
+assertEquals(2, bummer());
+assertEquals(3, bummer());
+%OptimizeFunctionOnNextCall(bummer);
+assertEquals(4, bummer());
+assertEquals(5, bummer());
+assertEquals(6, bummer());
diff --git a/test/preparser/testcfg.py b/test/preparser/testcfg.py
index 3e999f9..850c0a4 100644
--- a/test/preparser/testcfg.py
+++ b/test/preparser/testcfg.py
@@ -112,7 +112,7 @@
     with open(testcase.flags[0]) as f:
       return f.read()
 
-  def VariantFlags(self):
+  def VariantFlags(self, testcase, default_flags):
     return [[]];
 
 
diff --git a/tools/run-tests.py b/tools/run-tests.py
index 947e38c..dfe9036 100755
--- a/tools/run-tests.py
+++ b/tools/run-tests.py
@@ -332,8 +332,9 @@
     if options.cat:
       verbose.PrintTestSource(s.tests)
       continue
-    variant_flags = s.VariantFlags() or VARIANT_FLAGS
-    s.tests = [ t.CopyAddingFlags(v) for t in s.tests for v in variant_flags ]
+    s.tests = [ t.CopyAddingFlags(v)
+                for t in s.tests
+                for v in s.VariantFlags(t, VARIANT_FLAGS) ]
     s.tests = ShardTests(s.tests, options.shard_count, options.shard_run)
     num_tests += len(s.tests)
     for t in s.tests:
diff --git a/tools/testrunner/local/statusfile.py b/tools/testrunner/local/statusfile.py
index cc1e524..5f5533f 100644
--- a/tools/testrunner/local/statusfile.py
+++ b/tools/testrunner/local/statusfile.py
@@ -35,6 +35,7 @@
 CRASH = "CRASH"
 SLOW = "SLOW"
 FLAKY = "FLAKY"
+NO_VARIANTS = "NO_VARIANTS"
 # These are just for the status files and are mapped below in DEFS:
 FAIL_OK = "FAIL_OK"
 PASS_OR_FAIL = "PASS_OR_FAIL"
@@ -43,7 +44,7 @@
 
 KEYWORDS = {}
 for key in [SKIP, FAIL, PASS, OKAY, TIMEOUT, CRASH, SLOW, FLAKY, FAIL_OK,
-            PASS_OR_FAIL, ALWAYS]:
+            NO_VARIANTS, PASS_OR_FAIL, ALWAYS]:
   KEYWORDS[key] = key
 
 DEFS = {FAIL_OK: [FAIL, OKAY],
@@ -60,6 +61,10 @@
   return SKIP in outcomes or SLOW in outcomes
 
 
+def OnlyStandardVariant(outcomes):
+  return NO_VARIANTS in outcomes
+
+
 def IsFlaky(outcomes):
   return FLAKY in outcomes
 
diff --git a/tools/testrunner/local/testsuite.py b/tools/testrunner/local/testsuite.py
index b0372e7..8517ce9 100644
--- a/tools/testrunner/local/testsuite.py
+++ b/tools/testrunner/local/testsuite.py
@@ -74,8 +74,10 @@
   def ListTests(self, context):
     raise NotImplementedError
 
-  def VariantFlags(self):
-    return None
+  def VariantFlags(self, testcase, default_flags):
+    if testcase.outcomes and statusfile.OnlyStandardVariant(testcase.outcomes):
+      return [[]]
+    return default_flags
 
   def DownloadData(self):
     pass