@@ -464,5 +464,116 @@ UNINITIALIZED_TEST(TransitionArray_InsertToBinarySearchSizeAfterRehashing) {
464464 FreeCurrentEmbeddedBlob ();
465465}
466466
467+ UNINITIALIZED_TEST (TransitionArray_RehashNoDuplicatesWithinLinearSearchSize) {
468+ v8_flags.rehash_snapshot = true ;
469+ v8_flags.hash_seed = 42 ;
470+ v8_flags.allow_natives_syntax = true ;
471+ DisableEmbeddedBlobRefcounting ();
472+ v8::StartupData blob;
473+ // Stay within linear search size.
474+ constexpr int initial_size = TransitionArray::kMaxElementsForLinearSearch / 2 ;
475+
476+ {
477+ v8::Isolate::CreateParams testing_params;
478+ testing_params.array_buffer_allocator = CcTest::array_buffer_allocator ();
479+ v8::SnapshotCreator creator (testing_params);
480+ v8::Isolate* isolate = creator.GetIsolate ();
481+ {
482+ v8::HandleScope handle_scope (isolate);
483+ v8::Local<v8::Context> context = v8::Context::New (isolate);
484+ v8::Context::Scope context_scope (context);
485+ Isolate* i_isolate = reinterpret_cast <Isolate*>(isolate);
486+ v8::Local<v8::Object> obj = v8::Object::New (isolate);
487+ DirectHandle<Map> first_map =
488+ direct_handle (v8::Utils::OpenDirectHandle (*obj)->map (), i_isolate);
489+
490+ {
491+ TestTransitionsAccessor transitions (i_isolate, first_map);
492+ CHECK_EQ (0 , transitions.NumberOfTransitions ());
493+ }
494+
495+ // Insert transitions that will be rehashed on deserialization.
496+ v8::Local<v8::Value> null_value = v8::Null (isolate);
497+ v8::LocalVector<v8::Value> objects (isolate);
498+ for (int i = 0 ; i < initial_size; i++) {
499+ std::string prop_name = " prop_" + std::to_string (i);
500+ v8::Local<v8::String> name =
501+ v8::String::NewFromUtf8 (isolate, prop_name.c_str (),
502+ v8::NewStringType::kNormal )
503+ .ToLocalChecked ();
504+ v8::Local<v8::Object> new_obj = v8::Object::New (isolate);
505+ new_obj->Set (context, name, null_value).Check ();
506+ objects.push_back (new_obj);
507+ }
508+ context->Global ()
509+ ->Set (context, v8_str (" objects_for_transitions" ),
510+ v8::Array::New (isolate, objects.data (), objects.size ()))
511+ .Check ();
512+
513+ creator.SetDefaultContext (context);
514+
515+ TestTransitionsAccessor transitions (i_isolate, first_map);
516+ CHECK_EQ (initial_size, transitions.NumberOfTransitions ());
517+ }
518+ blob =
519+ creator.CreateBlob (v8::SnapshotCreator::FunctionCodeHandling::kClear );
520+ CHECK (blob.CanBeRehashed ());
521+ }
522+
523+ // Deserialize with a different hash seed to trigger rehashing.
524+ v8_flags.hash_seed = 1337 ;
525+ v8::Isolate::CreateParams testing_params;
526+ testing_params.array_buffer_allocator = CcTest::array_buffer_allocator ();
527+ testing_params.snapshot_blob = &blob;
528+ v8::Isolate* isolate = v8::Isolate::New (testing_params);
529+ {
530+ v8::Isolate::Scope isolate_scope (isolate);
531+ CHECK_EQ (static_cast <uint64_t >(1337 ),
532+ HashSeed (reinterpret_cast <Isolate*>(isolate)).seed ());
533+ v8::HandleScope handle_scope (isolate);
534+ v8::Local<v8::Context> context = v8::Context::New (isolate);
535+ CHECK (!context.IsEmpty ());
536+ v8::Context::Scope context_scope (context);
537+
538+ Isolate* i_isolate = reinterpret_cast <Isolate*>(isolate);
539+ v8::Local<v8::Object> obj = v8::Object::New (isolate);
540+ DirectHandle<Map> first_map =
541+ direct_handle (v8::Utils::OpenDirectHandle (*obj)->map (), i_isolate);
542+
543+ // Collect existing transition keys from the rehashed array.
544+ Handle<String> keys[initial_size];
545+ {
546+ TestTransitionsAccessor transitions (i_isolate, first_map);
547+ CHECK_EQ (initial_size, transitions.NumberOfTransitions ());
548+ for (int i = 0 ; i < initial_size; i++) {
549+ keys[i] = handle (Cast<String>(transitions.GetKey (i)), i_isolate);
550+ }
551+ }
552+
553+ // For each existing transition, create a fresh map and re-insert it with
554+ // the same field name.
555+ for (int i = 0 ; i < initial_size; i++) {
556+ Handle<Map> new_map =
557+ Map::CopyWithField (i_isolate, first_map, keys[i],
558+ FieldType::Any (i_isolate), NONE,
559+ PropertyConstness::kMutable ,
560+ Representation::Tagged (), OMIT_TRANSITION)
561+ .ToHandleChecked ();
562+ TransitionsAccessor::Insert (i_isolate, first_map, keys[i], new_map,
563+ PROPERTY_TRANSITION);
564+ }
565+
566+ // Verify no duplicates were created.
567+ {
568+ TestTransitionsAccessor transitions (i_isolate, first_map);
569+ CHECK_EQ (initial_size, transitions.NumberOfTransitions ());
570+ }
571+ }
572+
573+ isolate->Dispose ();
574+ delete[] blob.data ;
575+ FreeCurrentEmbeddedBlob ();
576+ }
577+
467578} // namespace internal
468579} // namespace v8
0 commit comments