diff --git a/src/field-index.h b/src/field-index.h index b4ef144577..a1552f050e 100644 --- a/src/field-index.h +++ b/src/field-index.h @@ -123,7 +123,8 @@ class FieldIndex final { }; // Offset of first inobject property from beginning of object. class FirstInobjectPropertyOffsetBits - : public BitField64 {}; + : public BitField64 {}; class IsHiddenField : public BitField64 {}; STATIC_ASSERT(IsHiddenField::kNext <= 64); diff --git a/src/objects-inl.h b/src/objects-inl.h index 2977dc9e48..6d4a63e37a 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -2440,6 +2440,7 @@ int ObjectTemplateInfo::embedder_field_count() const { } void ObjectTemplateInfo::set_embedder_field_count(int count) { + DCHECK_LE(count, JSObject::kMaxEmbedderFields); return set_data( Smi::FromInt(EmbedderFieldCount::update(Smi::ToInt(data()), count))); } diff --git a/src/objects.cc b/src/objects.cc index c7dadab394..5d2a77220d 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -13663,18 +13663,24 @@ void JSFunction::CalculateInstanceSizeHelper(InstanceType instance_type, int requested_in_object_properties, int* instance_size, int* in_object_properties) { + DCHECK_LE(static_cast(requested_embedder_fields), + JSObject::kMaxEmbedderFields); int header_size = JSObject::GetHeaderSize(instance_type, has_prototype_slot); int max_nof_fields = (JSObject::kMaxInstanceSize - header_size) >> kPointerSizeLog2; CHECK_LE(max_nof_fields, JSObject::kMaxInObjectProperties); - *in_object_properties = Min(requested_in_object_properties, max_nof_fields); - CHECK_LE(requested_embedder_fields, max_nof_fields - *in_object_properties); + CHECK_LE(static_cast(requested_embedder_fields), + static_cast(max_nof_fields)); + *in_object_properties = Min(requested_in_object_properties, + max_nof_fields - requested_embedder_fields); *instance_size = header_size + ((requested_embedder_fields + *in_object_properties) << kPointerSizeLog2); CHECK_EQ(*in_object_properties, ((*instance_size - header_size) >> kPointerSizeLog2) - requested_embedder_fields); + CHECK_LE(static_cast(*instance_size), + static_cast(JSObject::kMaxInstanceSize)); } // static diff --git a/src/objects.h b/src/objects.h index 60eb6080de..613e47aec4 100644 --- a/src/objects.h +++ b/src/objects.h @@ -2646,6 +2646,11 @@ class JSObject: public JSReceiver { static const int kMaxInObjectProperties = (kMaxInstanceSize - kHeaderSize) >> kPointerSizeLog2; STATIC_ASSERT(kMaxInObjectProperties <= kMaxNumberOfDescriptors); + // TODO(cbruni): Revisit calculation of the max supported embedder fields. + static const int kMaxEmbedderFields = + ((1 << kFirstInobjectPropertyOffsetBitCount) - 1 - kHeaderSize) >> + kPointerSizeLog2; + STATIC_ASSERT(kMaxEmbedderFields <= kMaxInObjectProperties); class BodyDescriptor; // No weak fields. diff --git a/src/property-details.h b/src/property-details.h index 34c43047f8..dbd4f93acd 100644 --- a/src/property-details.h +++ b/src/property-details.h @@ -197,6 +197,7 @@ class Representation { static const int kDescriptorIndexBitCount = 10; +static const int kFirstInobjectPropertyOffsetBitCount = 7; // The maximum number of descriptors we want in a descriptor array. It should // fit in a page and also the following should hold: // kMaxNumberOfDescriptors + kFieldsAdded <= PropertyArray::kMaxLength. diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index ed70a8cd01..a8cddb1dcd 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -2730,6 +2730,110 @@ THREADED_TEST(InternalFields) { CHECK_EQ(17, obj->GetInternalField(0)->Int32Value(env.local()).FromJust()); } +TEST(InternalFieldsSubclassing) { + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope scope(isolate); + for (int nof_embedder_fields = 0; + nof_embedder_fields < i::JSObject::kMaxEmbedderFields; + nof_embedder_fields++) { + Local templ = v8::FunctionTemplate::New(isolate); + Local instance_templ = templ->InstanceTemplate(); + instance_templ->SetInternalFieldCount(nof_embedder_fields); + Local constructor = + templ->GetFunction(env.local()).ToLocalChecked(); + // Check that instances have the correct NOF properties. + Local obj = + constructor->NewInstance(env.local()).ToLocalChecked(); + + i::Handle i_obj = + i::Handle::cast(v8::Utils::OpenHandle(*obj)); + CHECK_EQ(nof_embedder_fields, obj->InternalFieldCount()); + CHECK_EQ(0, i_obj->map()->GetInObjectProperties()); + // Check writing and reading internal fields. + for (int j = 0; j < nof_embedder_fields; j++) { + CHECK(obj->GetInternalField(j)->IsUndefined()); + int value = 17 + j; + obj->SetInternalField(j, v8_num(value)); + } + for (int j = 0; j < nof_embedder_fields; j++) { + int value = 17 + j; + CHECK_EQ(value, + obj->GetInternalField(j)->Int32Value(env.local()).FromJust()); + } + CHECK(env->Global() + ->Set(env.local(), v8_str("BaseClass"), constructor) + .FromJust()); + // Create various levels of subclasses to stress instance size calculation. + const int kMaxNofProperties = + i::JSObject::kMaxInObjectProperties - nof_embedder_fields; + // Select only a few values to speed up the test. + int sizes[] = {0, + 1, + 2, + 3, + 4, + 5, + 6, + kMaxNofProperties / 4, + kMaxNofProperties / 2, + kMaxNofProperties - 2, + kMaxNofProperties - 1, + kMaxNofProperties + 1, + kMaxNofProperties + 2, + kMaxNofProperties * 2, + kMaxNofProperties * 2}; + for (size_t i = 0; i < arraysize(sizes); i++) { + int nof_properties = sizes[i]; + bool in_object_only = nof_properties <= kMaxNofProperties; + std::ostringstream src; + // Assembler source string for a subclass with {nof_properties} + // in-object properties. + src << "(function() {\n" + << " class SubClass extends BaseClass {\n" + << " constructor() {\n" + << " super();\n"; + // Set {nof_properties} instance properties in the constructor. + for (int j = 0; j < nof_properties; j++) { + src << " this.property" << j << " = " << j << ";\n"; + } + src << " }\n" + << " };\n" + << " let instance;\n" + << " for (let i = 0; i < 3; i++) {\n" + << " instance = new SubClass();\n" + << " }" + << " return instance;\n" + << "})();"; + Local value = CompileRun(src.str().c_str()).As(); + + i::Handle i_value = + i::Handle::cast(v8::Utils::OpenHandle(*value)); +#ifdef VERIFY_HEAP + i_value->HeapObjectVerify(); + i_value->map()->HeapObjectVerify(); + i_value->map()->FindRootMap()->HeapObjectVerify(); +#endif + CHECK_EQ(nof_embedder_fields, value->InternalFieldCount()); + if (in_object_only) { + CHECK_LE(nof_properties, i_value->map()->GetInObjectProperties()); + } else { + CHECK_LE(kMaxNofProperties, i_value->map()->GetInObjectProperties()); + } + + // Make Sure we get the precise property count. + i_value->map()->FindRootMap()->CompleteInobjectSlackTracking(); + // TODO(cbruni): fix accounting to make this condition true. + // CHECK_EQ(0, i_value->map()->UnusedPropertyFields()); + if (in_object_only) { + CHECK_EQ(nof_properties, i_value->map()->GetInObjectProperties()); + } else { + CHECK_LE(kMaxNofProperties, i_value->map()->GetInObjectProperties()); + } + } + } +} + THREADED_TEST(InternalFieldsOfRegularObjects) { LocalContext env; v8::Isolate* isolate = env->GetIsolate();