Revision: 23210
Author:   [email protected]
Date:     Tue Aug 19 17:02:04 2014 UTC
Log:      Use LookupIterator to transition to accessors

BUG=
[email protected]

Review URL: https://codereview.chromium.org/490533002
http://code.google.com/p/v8/source/detail?r=23210

Added:
 /branches/bleeding_edge/test/mjsunit/deopt-global-accessor.js
Modified:
 /branches/bleeding_edge/src/lookup-inl.h
 /branches/bleeding_edge/src/lookup.cc
 /branches/bleeding_edge/src/lookup.h
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/stub-cache.cc
 /branches/bleeding_edge/src/stub-cache.h
 /branches/bleeding_edge/src/v8natives.js

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/deopt-global-accessor.js Tue Aug 19 17:02:04 2014 UTC
@@ -0,0 +1,23 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax
+
+x = 1;
+x = 2;
+x = 3;
+
+function f() {
+  return x;
+}
+
+f();
+f();
+f();
+%OptimizeFunctionOnNextCall(f);
+f();
+
+Object.defineProperty(this, "x", {get:function() { return 100; }});
+
+assertEquals(100, f());
=======================================
--- /branches/bleeding_edge/src/lookup-inl.h    Tue Aug  5 09:32:55 2014 UTC
+++ /branches/bleeding_edge/src/lookup-inl.h    Tue Aug 19 17:02:04 2014 UTC
@@ -29,6 +29,7 @@


 LookupIterator::State LookupIterator::LookupInHolder(Map* map) {
+  STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
   DisallowHeapAllocation no_gc;
   switch (state_) {
     case NOT_FOUND:
=======================================
--- /branches/bleeding_edge/src/lookup.cc       Mon Aug 18 14:59:04 2014 UTC
+++ /branches/bleeding_edge/src/lookup.cc       Tue Aug 19 17:02:04 2014 UTC
@@ -5,6 +5,7 @@
 #include "src/v8.h"

 #include "src/bootstrapper.h"
+#include "src/deoptimizer.h"
 #include "src/lookup.h"
 #include "src/lookup-inl.h"

@@ -107,6 +108,14 @@
   has_property_ = true;
   return true;
 }
+
+
+void LookupIterator::ReloadPropertyInformation() {
+  state_ = BEFORE_PROPERTY;
+  state_ = LookupInHolder(*holder_map_);
+  DCHECK(IsFound());
+  HasProperty();
+}


 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
@@ -116,13 +125,7 @@
   holder_map_ =
       Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
   JSObject::MigrateToMap(GetHolder<JSObject>(), holder_map_);
-  // Reload property information.
-  if (holder_map_->is_dictionary_map()) {
-    property_encoding_ = DICTIONARY;
-  } else {
-    property_encoding_ = DESCRIPTOR;
-  }
-  CHECK(HasProperty());
+  ReloadPropertyInformation();
 }


@@ -137,17 +140,12 @@
     JSObject::MigrateToMap(holder, holder_map_);
   }

-  // Reload property information and update the descriptor if in dictionary
-  // mode.
   if (holder_map_->is_dictionary_map()) {
-    property_encoding_ = DICTIONARY;
     PropertyDetails details(attributes, NORMAL, 0);
     JSObject::SetNormalizedProperty(holder, name(), value, details);
-  } else {
-    property_encoding_ = DESCRIPTOR;
   }

-  CHECK(HasProperty());
+  ReloadPropertyInformation();
 }


@@ -172,12 +170,62 @@
value, attributes, store_mode);
   JSObject::MigrateToMap(receiver, holder_map_);

-  // Reload the information.
-  state_ = NOT_FOUND;
-  configuration_ = CHECK_PROPERTY;
-  state_ = LookupInHolder(*holder_map_);
-  DCHECK(IsFound());
-  HasProperty();
+  ReloadPropertyInformation();
+}
+
+
+void LookupIterator::TransitionToAccessorProperty(
+    AccessorComponent component, Handle<Object> accessor,
+    PropertyAttributes attributes) {
+  DCHECK(!accessor->IsNull());
+  // Can only be called when the receiver is a JSObject. JSProxy has to be
+  // handled via a trap. Adding properties to primitive values is not
+  // observable.
+  Handle<JSObject> receiver = Handle<JSObject>::cast(GetReceiver());
+
+  if (receiver->IsJSGlobalProxy()) {
+    PrototypeIterator iter(isolate(), receiver);
+    receiver =
+        Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter));
+  }
+
+  maybe_holder_ = receiver;
+  holder_map_ = Map::TransitionToAccessorProperty(
+      handle(receiver->map()), name_, component, accessor, attributes);
+  JSObject::MigrateToMap(receiver, holder_map_);
+
+  ReloadPropertyInformation();
+
+  if (!holder_map_->is_dictionary_map()) return;
+
+  // We have to deoptimize since accesses to data properties may have been
+  // inlined without a corresponding map-check.
+  if (holder_map_->IsGlobalObjectMap()) {
+    Deoptimizer::DeoptimizeGlobalObject(*receiver);
+  }
+
+  // Install the accessor into the dictionary-mode object.
+  PropertyDetails details(attributes, CALLBACKS, 0);
+  Handle<AccessorPair> pair;
+  if (IsFound() && HasProperty() && property_kind() == ACCESSOR &&
+      GetAccessors()->IsAccessorPair()) {
+    pair = Handle<AccessorPair>::cast(GetAccessors());
+ // If the component and attributes are identical, nothing has to be done.
+    if (pair->get(component) == *accessor) {
+      if (property_details().attributes() == attributes) return;
+    } else {
+      pair = AccessorPair::Copy(pair);
+      pair->set(component, *accessor);
+    }
+  } else {
+    pair = isolate()->factory()->NewAccessorPair();
+    pair->set(component, *accessor);
+  }
+  JSObject::SetNormalizedProperty(receiver, name_, pair, details);
+
+  JSObject::ReoptimizeIfPrototype(receiver);
+  holder_map_ = handle(receiver->map());
+  ReloadPropertyInformation();
 }


=======================================
--- /branches/bleeding_edge/src/lookup.h        Mon Aug 18 15:08:14 2014 UTC
+++ /branches/bleeding_edge/src/lookup.h        Tue Aug 19 17:02:04 2014 UTC
@@ -31,11 +31,14 @@
   };

   enum State {
+    ACCESS_CHECK,
+    INTERCEPTOR,
+    JSPROXY,
     NOT_FOUND,
     PROPERTY,
-    INTERCEPTOR,
-    ACCESS_CHECK,
-    JSPROXY
+ // Set state_ to BEFORE_PROPERTY to ensure that the next lookup will be a
+    // PROPERTY lookup.
+    BEFORE_PROPERTY = INTERCEPTOR
   };

   enum PropertyKind {
@@ -100,6 +103,10 @@

   bool IsFound() const { return state_ != NOT_FOUND; }
   void Next();
+  void NotFound() {
+    has_property_ = false;
+    state_ = NOT_FOUND;
+  }

   Heap* heap() const { return isolate_->heap(); }
   Factory* factory() const { return isolate_->factory(); }
@@ -130,6 +137,9 @@
                                 Object::StoreFromKeyed store_mode);
   void ReconfigureDataProperty(Handle<Object> value,
                                PropertyAttributes attributes);
+  void TransitionToAccessorProperty(AccessorComponent component,
+                                    Handle<Object> accessor,
+                                    PropertyAttributes attributes);
   PropertyKind property_kind() const {
     DCHECK(has_property_);
     return property_kind_;
@@ -162,6 +172,7 @@
   MUST_USE_RESULT inline JSReceiver* NextHolder(Map* map);
   inline State LookupInHolder(Map* map);
   Handle<Object> FetchValue() const;
+  void ReloadPropertyInformation();

   bool IsBootstrapping() const;

=======================================
--- /branches/bleeding_edge/src/objects.cc      Tue Aug 19 14:58:41 2014 UTC
+++ /branches/bleeding_edge/src/objects.cc      Tue Aug 19 17:02:04 2014 UTC
@@ -6100,51 +6100,6 @@

   SetElementCallback(object, index, accessors, attributes);
 }
-
-
-Handle<AccessorPair> JSObject::CreateAccessorPairFor(Handle<JSObject> object,
-                                                     Handle<Name> name) {
-  Isolate* isolate = object->GetIsolate();
-  LookupResult result(isolate);
-  object->LookupOwnRealNamedProperty(name, &result);
-  if (result.IsPropertyCallbacks()) {
- // Note that the result can actually have IsDontDelete() == true when we
-    // e.g. have to fall back to the slow case while adding a setter after
- // successfully reusing a map transition for a getter. Nevertheless, this is
-    // OK, because the assertion only holds for the whole addition of both
-    // accessors, not for the addition of each part. See first comment in
-    // DefinePropertyAccessor below.
-    Object* obj = result.GetCallbackObject();
-    if (obj->IsAccessorPair()) {
-      return AccessorPair::Copy(handle(AccessorPair::cast(obj), isolate));
-    }
-  }
-  return isolate->factory()->NewAccessorPair();
-}
-
-
-void JSObject::DefinePropertyAccessor(Handle<JSObject> object,
-                                      Handle<Name> name,
-                                      Handle<Object> getter,
-                                      Handle<Object> setter,
-                                      PropertyAttributes attributes) {
- // We could assert that the property is configurable here, but we would need
-  // to do a lookup, which seems to be a bit of overkill.
-  bool only_attribute_changes = getter->IsNull() && setter->IsNull();
-  if (object->HasFastProperties() && !only_attribute_changes &&
- (object->map()->NumberOfOwnDescriptors() <= kMaxNumberOfDescriptors)) {
-    bool getterOk = getter->IsNull() ||
- DefineFastAccessor(object, name, ACCESSOR_GETTER, getter, attributes);
-    bool setterOk = !getterOk || setter->IsNull() ||
- DefineFastAccessor(object, name, ACCESSOR_SETTER, setter, attributes);
-    if (getterOk && setterOk) return;
-  }
-
-  Handle<AccessorPair> accessors = CreateAccessorPairFor(object, name);
-  accessors->SetComponents(*getter, *setter);
-
-  SetPropertyCallback(object, name, accessors, attributes);
-}


 bool Map::DictionaryElementsInPrototypeChainOnly() {
@@ -6305,7 +6260,19 @@
   if (is_element) {
     DefineElementAccessor(object, index, getter, setter, attributes);
   } else {
-    DefinePropertyAccessor(object, name, getter, setter, attributes);
+    DCHECK(getter->IsSpecFunction() || getter->IsUndefined() ||
+           getter->IsNull());
+    DCHECK(setter->IsSpecFunction() || setter->IsUndefined() ||
+           setter->IsNull());
+    // At least one of the accessors needs to be a new value.
+    DCHECK(!getter->IsNull() || !setter->IsNull());
+    LookupIterator it(object, name, LookupIterator::CHECK_PROPERTY);
+    if (!getter->IsNull()) {
+      it.TransitionToAccessorProperty(ACCESSOR_GETTER, getter, attributes);
+    }
+    if (!setter->IsNull()) {
+      it.TransitionToAccessorProperty(ACCESSOR_SETTER, setter, attributes);
+    }
   }

   if (is_observed) {
@@ -6315,111 +6282,6 @@

   return isolate->factory()->undefined_value();
 }
-
-
-static bool TryAccessorTransition(Handle<JSObject> self,
-                                  Handle<Map> transitioned_map,
-                                  int target_descriptor,
-                                  AccessorComponent component,
-                                  Handle<Object> accessor,
-                                  PropertyAttributes attributes) {
-  DescriptorArray* descs = transitioned_map->instance_descriptors();
-  PropertyDetails details = descs->GetDetails(target_descriptor);
-
- // If the transition target was not callbacks, fall back to the slow case.
-  if (details.type() != CALLBACKS) return false;
-  Object* descriptor = descs->GetCallbacksObject(target_descriptor);
-  if (!descriptor->IsAccessorPair()) return false;
-
-  Object* target_accessor = AccessorPair::cast(descriptor)->get(component);
-  PropertyAttributes target_attributes = details.attributes();
-
-  // Reuse transition if adding same accessor with same attributes.
-  if (target_accessor == *accessor && target_attributes == attributes) {
-    JSObject::MigrateToMap(self, transitioned_map);
-    return true;
-  }
-
- // If either not the same accessor, or not the same attributes, fall back to
-  // the slow case.
-  return false;
-}
-
-
-bool JSObject::DefineFastAccessor(Handle<JSObject> object,
-                                  Handle<Name> name,
-                                  AccessorComponent component,
-                                  Handle<Object> accessor,
-                                  PropertyAttributes attributes) {
-  DCHECK(accessor->IsSpecFunction() || accessor->IsUndefined());
-  Isolate* isolate = object->GetIsolate();
-  LookupResult result(isolate);
-  object->LookupOwn(name, &result);
-
-  if (result.IsFound() && !result.IsPropertyCallbacks()) {
-    return false;
-  }
-
- // Return success if the same accessor with the same attributes already exist.
-  AccessorPair* source_accessors = NULL;
-  if (result.IsPropertyCallbacks()) {
-    Object* callback_value = result.GetCallbackObject();
-    if (callback_value->IsAccessorPair()) {
-      source_accessors = AccessorPair::cast(callback_value);
-      Object* entry = source_accessors->get(component);
-      if (entry == *accessor && result.GetAttributes() == attributes) {
-        return true;
-      }
-    } else {
-      return false;
-    }
-
-    int descriptor_number = result.GetDescriptorIndex();
-
-    object->map()->LookupTransition(*object, *name, &result);
-
-    if (result.IsFound()) {
-      Handle<Map> target(result.GetTransitionTarget());
-      DCHECK(target->NumberOfOwnDescriptors() ==
-             object->map()->NumberOfOwnDescriptors());
-      // This works since descriptors are sorted in order of addition.
-      DCHECK(Name::Equals(
-          handle(object->map()->instance_descriptors()->GetKey(
-              descriptor_number)),
-          name));
-      return TryAccessorTransition(object, target, descriptor_number,
-                                   component, accessor, attributes);
-    }
-  } else {
-    // If not, lookup a transition.
-    object->map()->LookupTransition(*object, *name, &result);
-
-    // If there is a transition, try to follow it.
-    if (result.IsFound()) {
-      Handle<Map> target(result.GetTransitionTarget());
-      int descriptor_number = target->LastAdded();
-      DCHECK(Name::Equals(name,
- handle(target->instance_descriptors()->GetKey(descriptor_number))));
-      return TryAccessorTransition(object, target, descriptor_number,
-                                   component, accessor, attributes);
-    }
-  }
-
- // If there is no transition yet, add a transition to the a new accessor pair
-  // containing the accessor.  Allocate a new pair if there were no source
-  // accessors.  Otherwise, copy the pair and modify the accessor.
-  Handle<AccessorPair> accessors = source_accessors != NULL
-      ? AccessorPair::Copy(Handle<AccessorPair>(source_accessors))
-      : isolate->factory()->NewAccessorPair();
-  accessors->set(component, *accessor);
-
-  CallbacksDescriptor new_accessors_desc(name, accessors, attributes);
-  Handle<Map> new_map = Map::CopyInsertDescriptor(
-      handle(object->map()), &new_accessors_desc, INSERT_TRANSITION);
-
-  JSObject::MigrateToMap(object, new_map);
-  return true;
-}


 MaybeHandle<Object> JSObject::SetAccessor(Handle<JSObject> object,
@@ -7060,6 +6922,101 @@
   return CopyGeneralizeAllRepresentations(map, descriptor, FORCE_FIELD,
attributes, "attributes mismatch");
 }
+
+
+Handle<Map> Map::TransitionToAccessorProperty(Handle<Map> map,
+                                              Handle<Name> name,
+                                              AccessorComponent component,
+                                              Handle<Object> accessor,
+ PropertyAttributes attributes) {
+  Isolate* isolate = name->GetIsolate();
+
+  // Dictionary maps can always have additional data properties.
+  if (map->is_dictionary_map()) {
+ // For global objects, property cells are inlined. We need to change the
+    // map.
+    if (map->IsGlobalObjectMap()) return Copy(map);
+    return map;
+  }
+
+  // Migrate to the newest map before transitioning to the new property.
+  if (map->is_deprecated()) map = Update(map);
+
+  PropertyNormalizationMode mode = map->is_prototype_map()
+                                       ? KEEP_INOBJECT_PROPERTIES
+                                       : CLEAR_INOBJECT_PROPERTIES;
+
+  int index = map->SearchTransition(*name);
+  if (index != TransitionArray::kNotFound) {
+    Handle<Map> transition(map->GetTransition(index));
+    DescriptorArray* descriptors = transition->instance_descriptors();
+    // Fast path, assume that we're modifying the last added descriptor.
+    int descriptor = transition->LastAdded();
+    if (descriptors->GetKey(descriptor) != *name) {
+      // If not, search for the descriptor.
+      descriptor = descriptors->SearchWithCache(*name, *transition);
+    }
+
+    if (descriptors->GetDetails(descriptor).type() != CALLBACKS) {
+      return Map::Normalize(map, mode);
+    }
+
+    // TODO(verwaest): Handle attributes better.
+    if (descriptors->GetDetails(descriptor).attributes() != attributes) {
+      return Map::Normalize(map, mode);
+    }
+
+    Handle<Object> maybe_pair(descriptors->GetValue(descriptor), isolate);
+    if (!maybe_pair->IsAccessorPair()) {
+      return Map::Normalize(map, mode);
+    }
+
+    Handle<AccessorPair> pair = Handle<AccessorPair>::cast(maybe_pair);
+    if (pair->get(component) != *accessor) {
+      return Map::Normalize(map, mode);
+    }
+
+    return transition;
+  }
+
+  Handle<AccessorPair> pair;
+  DescriptorArray* old_descriptors = map->instance_descriptors();
+  int descriptor = old_descriptors->SearchWithCache(*name, *map);
+  if (descriptor != DescriptorArray::kNotFound) {
+    PropertyDetails old_details = old_descriptors->GetDetails(descriptor);
+    if (old_details.type() != CALLBACKS) {
+      return Map::Normalize(map, mode);
+    }
+
+    if (old_details.attributes() != attributes) {
+      return Map::Normalize(map, mode);
+    }
+
+ Handle<Object> maybe_pair(old_descriptors->GetValue(descriptor), isolate);
+    if (!maybe_pair->IsAccessorPair()) {
+      return Map::Normalize(map, mode);
+    }
+
+ Object* current = Handle<AccessorPair>::cast(maybe_pair)->get(component);
+    if (current == *accessor) return map;
+
+    if (!current->IsTheHole()) {
+      return Map::Normalize(map, mode);
+    }
+
+    pair = AccessorPair::Copy(Handle<AccessorPair>::cast(maybe_pair));
+  } else if (map->NumberOfOwnDescriptors() >= kMaxNumberOfDescriptors ||
+             map->TooManyFastProperties(CERTAINLY_NOT_STORE_FROM_KEYED)) {
+    return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES);
+  } else {
+    pair = isolate->factory()->NewAccessorPair();
+  }
+
+  pair->set(component, *accessor);
+  TransitionFlag flag = INSERT_TRANSITION;
+  CallbacksDescriptor new_desc(name, pair, attributes);
+  return Map::CopyInsertDescriptor(map, &new_desc, flag);
+}


 Handle<Map> Map::CopyAddDescriptor(Handle<Map> map,
=======================================
--- /branches/bleeding_edge/src/objects.h       Tue Aug 19 14:58:41 2014 UTC
+++ /branches/bleeding_edge/src/objects.h       Tue Aug 19 17:02:04 2014 UTC
@@ -2724,22 +2724,6 @@
                                     Handle<Object> getter,
                                     Handle<Object> setter,
                                     PropertyAttributes attributes);
- static Handle<AccessorPair> CreateAccessorPairFor(Handle<JSObject> object,
-                                                    Handle<Name> name);
-  static void DefinePropertyAccessor(Handle<JSObject> object,
-                                     Handle<Name> name,
-                                     Handle<Object> getter,
-                                     Handle<Object> setter,
-                                     PropertyAttributes attributes);
-
-  // Try to define a single accessor paying attention to map transitions.
- // Returns false if this was not possible and we have to use the slow case.
-  static bool DefineFastAccessor(Handle<JSObject> object,
-                                 Handle<Name> name,
-                                 AccessorComponent component,
-                                 Handle<Object> accessor,
-                                 PropertyAttributes attributes);
-

// Return the hash table backing store or the inline stored identity hash,
   // whatever is found.
@@ -6462,6 +6446,9 @@
                                               Handle<Object> value,
PropertyAttributes attributes,
                                               StoreFromKeyed store_mode);
+  static Handle<Map> TransitionToAccessorProperty(
+      Handle<Map> map, Handle<Name> name, AccessorComponent component,
+      Handle<Object> accessor, PropertyAttributes attributes);
static Handle<Map> ReconfigureDataProperty(Handle<Map> map, int descriptor, PropertyAttributes attributes);

=======================================
--- /branches/bleeding_edge/src/runtime.cc      Tue Aug 19 14:55:47 2014 UTC
+++ /branches/bleeding_edge/src/runtime.cc      Tue Aug 19 17:02:04 2014 UTC
@@ -4948,7 +4948,7 @@
 // Transform getter or setter into something DefineAccessor can handle.
 static Handle<Object> InstantiateAccessorComponent(Isolate* isolate,
Handle<Object> component) {
-  if (component->IsUndefined()) return isolate->factory()->null_value();
+ if (component->IsUndefined()) return isolate->factory()->undefined_value();
   Handle<FunctionTemplateInfo> info =
       Handle<FunctionTemplateInfo>::cast(component);
   return Utils::OpenHandle(*Utils::ToLocal(info)->GetFunction());
=======================================
--- /branches/bleeding_edge/src/stub-cache.cc   Mon Aug 18 15:03:13 2014 UTC
+++ /branches/bleeding_edge/src/stub-cache.cc   Tue Aug 19 17:02:04 2014 UTC
@@ -1191,18 +1191,6 @@
     MacroAssembler* masm) {
   KeyedStoreIC::GenerateSlow(masm);
 }
-
-
-CallOptimization::CallOptimization(LookupResult* lookup) {
-  if (lookup->IsFound() &&
-      lookup->IsCacheable() &&
-      lookup->IsConstantFunction()) {
-    // We only optimize constant function calls.
-    Initialize(Handle<JSFunction>(lookup->GetConstantFunction()));
-  } else {
-    Initialize(Handle<JSFunction>::null());
-  }
-}


 CallOptimization::CallOptimization(Handle<JSFunction> function) {
=======================================
--- /branches/bleeding_edge/src/stub-cache.h    Mon Aug 18 15:03:13 2014 UTC
+++ /branches/bleeding_edge/src/stub-cache.h    Tue Aug 19 17:02:04 2014 UTC
@@ -626,8 +626,6 @@
 // Holds information about possible function call optimizations.
 class CallOptimization BASE_EMBEDDED {
  public:
-  explicit CallOptimization(LookupResult* lookup);
-
   explicit CallOptimization(Handle<JSFunction> function);

   bool is_constant_call() const {
=======================================
--- /branches/bleeding_edge/src/v8natives.js    Mon Aug 11 19:24:05 2014 UTC
+++ /branches/bleeding_edge/src/v8natives.js    Tue Aug 19 17:02:04 2014 UTC
@@ -840,8 +840,18 @@
     //                 property.
     // Step 12 - updating an existing accessor property with an accessor
     //           descriptor.
-    var getter = desc.hasGetter() ? desc.getGet() : null;
-    var setter = desc.hasSetter() ? desc.getSet() : null;
+    var getter = null;
+    if (desc.hasGetter()) {
+      getter = desc.getGet();
+    } else if (IsAccessorDescriptor(current) && current.hasGetter()) {
+      getter = current.getGet();
+    }
+    var setter = null;
+    if (desc.hasSetter()) {
+      setter = desc.getSet();
+    } else if (IsAccessorDescriptor(current) && current.hasSetter()) {
+      setter = current.getSet();
+    }
     %DefineAccessorPropertyUnchecked(obj, p, getter, setter, flag);
   }
   return true;

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to