From 22f2b13fa8df33e4a0dc58860577a242a4aa5f86 Mon Sep 17 00:00:00 2001 From: jkummerow Date: Wed, 29 Apr 2015 02:31:38 -0700 Subject: [PATCH] Fix unobservable constructor replacement on prototype maps 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 | 1 + src/objects-debug.cc | 1 + src/objects-inl.h | 1 + src/objects-printer.cc | 1 + src/objects.cc | 23 +++++++++++++++++------ src/objects.h | 5 ++++- test/cctest/test-api.cc | 21 ++++++++++++++------- 7 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/factory.cc b/src/factory.cc index 133f74d..ddb6a64 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -56,6 +56,7 @@ Handle Factory::NewPrototypeInfo() { Handle::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; } diff --git a/src/objects-debug.cc b/src/objects-debug.cc index eebf00b..de16dee 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -887,6 +887,7 @@ void PrototypeInfo::PrototypeInfoVerify() { CHECK(prototype_users()->IsSmi()); } CHECK(validity_cell()->IsCell() || validity_cell()->IsSmi()); + VerifyPointer(constructor_name()); } diff --git a/src/objects-inl.h b/src/objects-inl.h index 61dfa44..6f97245 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -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) diff --git a/src/objects-printer.cc b/src/objects-printer.cc index cb3f27a..00a1002 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -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"; } diff --git a/src/objects.cc b/src/objects.cc index 9b0e8a4..feab8f4 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -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 object, Handle 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 constructor_name(object->constructor_name(), isolate); Context* context = constructor->context()->native_context(); JSFunction* object_function = context->object_function(); object->map()->SetConstructor(object_function); + Handle proto_info = + Map::GetOrCreatePrototypeInfo(object, isolate); + proto_info->set_constructor_name(*constructor_name); } } - object->map()->set_is_prototype_map(true); } } diff --git a/src/objects.h b/src/objects.h index bcd426e..7244d17 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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); diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 3f568be..7fd75a2 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -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 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 child_prototype = context->Global()->Get(v8_str("proto")); + CHECK(child_prototype->IsObject() && + child_prototype->ToObject(isolate)->GetConstructorName()->Equals( + v8_str("Parent"))); } -- 2.7.4