Fix unobservable constructor replacement on prototype maps
authorjkummerow <jkummerow@chromium.org>
Wed, 29 Apr 2015 09:31:38 +0000 (02:31 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 29 Apr 2015 09:31:51 +0000 (09:31 +0000)
BUG=chromium:478522
LOG=y
R=verwaest@chromium.org

Review URL: https://codereview.chromium.org/1097113003

Cr-Commit-Position: refs/heads/master@{#28126}

src/factory.cc
src/objects-debug.cc
src/objects-inl.h
src/objects-printer.cc
src/objects.cc
src/objects.h
test/cctest/test-api.cc

index 133f74d9d6d21d8cb9dc32de838aa4cd4f4b2101..ddb6a648e3613019d0a17ace06cb8d398bcfd674 100644 (file)
@@ -56,6 +56,7 @@ Handle<PrototypeInfo> Factory::NewPrototypeInfo() {
       Handle<PrototypeInfo>::cast(NewStruct(PROTOTYPE_INFO_TYPE));
   result->set_prototype_users(WeakFixedArray::Empty());
   result->set_validity_cell(Smi::FromInt(0));
+  result->set_constructor_name(Smi::FromInt(0));
   return result;
 }
 
index eebf00bcda4d4332dd7b33239ac9be188f92196f..de16dee26b368d98813b728697b3ddbd345a782e 100644 (file)
@@ -887,6 +887,7 @@ void PrototypeInfo::PrototypeInfoVerify() {
     CHECK(prototype_users()->IsSmi());
   }
   CHECK(validity_cell()->IsCell() || validity_cell()->IsSmi());
+  VerifyPointer(constructor_name());
 }
 
 
index 61dfa44a3ff0abdef8dcaa232ef0f7c9eb53a043..6f97245e766358c21a7e4eb9bc00dc2a4de7d6b8 100644 (file)
@@ -5461,6 +5461,7 @@ ACCESSORS(Box, value, Object, kValueOffset)
 
 ACCESSORS(PrototypeInfo, prototype_users, Object, kPrototypeUsersOffset)
 ACCESSORS(PrototypeInfo, validity_cell, Object, kValidityCellOffset)
+ACCESSORS(PrototypeInfo, constructor_name, Object, kConstructorNameOffset)
 
 ACCESSORS(AccessorPair, getter, Object, kGetterOffset)
 ACCESSORS(AccessorPair, setter, Object, kSetterOffset)
index cb3f27a79b0e3f8ece986a4df7bbad7d991863fe..00a1002e0d86130bf0fdda41db7488fd8cbffd98 100644 (file)
@@ -881,6 +881,7 @@ void PrototypeInfo::PrototypeInfoPrint(std::ostream& os) {  // NOLINT
   HeapObject::PrintHeader(os, "PrototypeInfo");
   os << "\n - prototype users: " << Brief(prototype_users());
   os << "\n - validity cell: " << Brief(validity_cell());
+  os << "\n - constructor name: " << Brief(constructor_name());
   os << "\n";
 }
 
index 9b0e8a4acd0f17f2c7e16d91afe04ac34170dbd8..feab8f428cf64cbdb5a58ab172054eec7f11ba12 100644 (file)
@@ -1697,6 +1697,12 @@ String* JSReceiver::class_name() {
 
 
 String* Map::constructor_name() {
+  if (is_prototype_map() && prototype_info()->IsPrototypeInfo()) {
+    PrototypeInfo* proto_info = PrototypeInfo::cast(prototype_info());
+    if (proto_info->constructor_name()->IsString()) {
+      return String::cast(proto_info->constructor_name());
+    }
+  }
   Object* maybe_constructor = GetConstructor();
   if (maybe_constructor->IsJSFunction()) {
     JSFunction* constructor = JSFunction::cast(maybe_constructor);
@@ -10062,21 +10068,26 @@ void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
       Handle<Map> new_map = Map::Copy(handle(object->map()), "CopyAsPrototype");
       JSObject::MigrateToMap(object, new_map);
     }
+    object->map()->set_is_prototype_map(true);
+
+    // Replace the pointer to the exact constructor with the Object function
+    // from the same context if undetectable from JS. This is to avoid keeping
+    // memory alive unnecessarily.
     Object* maybe_constructor = object->map()->GetConstructor();
     if (maybe_constructor->IsJSFunction()) {
       JSFunction* constructor = JSFunction::cast(maybe_constructor);
-      // Replace the pointer to the exact constructor with the Object function
-      // from the same context if undetectable from JS. This is to avoid keeping
-      // memory alive unnecessarily.
+      Isolate* isolate = object->GetIsolate();
       if (!constructor->shared()->IsApiFunction() &&
-          object->class_name() ==
-              object->GetIsolate()->heap()->Object_string()) {
+          object->class_name() == isolate->heap()->Object_string()) {
+        Handle<String> constructor_name(object->constructor_name(), isolate);
         Context* context = constructor->context()->native_context();
         JSFunction* object_function = context->object_function();
         object->map()->SetConstructor(object_function);
+        Handle<PrototypeInfo> proto_info =
+            Map::GetOrCreatePrototypeInfo(object, isolate);
+        proto_info->set_constructor_name(*constructor_name);
       }
     }
-    object->map()->set_is_prototype_map(true);
   }
 }
 
index bcd426e1fa889bf8a6a5e3c73cb7063910f2bb34..7244d17b0cceaf663bb107656d6642062127ba68 100644 (file)
@@ -6642,6 +6642,8 @@ class PrototypeInfo : public Struct {
   // [validity_cell]: Cell containing the validity bit for prototype chains
   // going through this object, or Smi(0) if uninitialized.
   DECL_ACCESSORS(validity_cell, Object)
+  // [constructor_name]: User-friendly name of the original constructor.
+  DECL_ACCESSORS(constructor_name, Object)
 
   DECLARE_CAST(PrototypeInfo)
 
@@ -6651,7 +6653,8 @@ class PrototypeInfo : public Struct {
 
   static const int kPrototypeUsersOffset = HeapObject::kHeaderSize;
   static const int kValidityCellOffset = kPrototypeUsersOffset + kPointerSize;
-  static const int kSize = kValidityCellOffset + kPointerSize;
+  static const int kConstructorNameOffset = kValidityCellOffset + kPointerSize;
+  static const int kSize = kConstructorNameOffset + kPointerSize;
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(PrototypeInfo);
index 3f568beb4a8f1404aae54804e886aa04c321ac6f..7fd75a2c78e5c4cfe3de09c88a2c127fb1329389 100644 (file)
@@ -11265,13 +11265,15 @@ THREADED_TEST(ObjectGetConstructorName) {
   v8::Isolate* isolate = CcTest::isolate();
   LocalContext context;
   v8::HandleScope scope(isolate);
-  v8_compile("function Parent() {};"
-             "function Child() {};"
-             "Child.prototype = new Parent();"
-             "var outer = { inner: function() { } };"
-             "var p = new Parent();"
-             "var c = new Child();"
-             "var x = new outer.inner();")->Run();
+  v8_compile(
+      "function Parent() {};"
+      "function Child() {};"
+      "Child.prototype = new Parent();"
+      "var outer = { inner: function() { } };"
+      "var p = new Parent();"
+      "var c = new Child();"
+      "var x = new outer.inner();"
+      "var proto = Child.prototype;")->Run();
 
   Local<v8::Value> p = context->Global()->Get(v8_str("p"));
   CHECK(p->IsObject() &&
@@ -11285,6 +11287,11 @@ THREADED_TEST(ObjectGetConstructorName) {
   CHECK(x->IsObject() &&
         x->ToObject(isolate)->GetConstructorName()->Equals(
             v8_str("outer.inner")));
+
+  Local<v8::Value> child_prototype = context->Global()->Get(v8_str("proto"));
+  CHECK(child_prototype->IsObject() &&
+        child_prototype->ToObject(isolate)->GetConstructorName()->Equals(
+            v8_str("Parent")));
 }