diff options
Diffstat (limited to 'src/mutator.cc')
-rw-r--r-- | src/mutator.cc | 609 |
1 files changed, 348 insertions, 261 deletions
diff --git a/src/mutator.cc b/src/mutator.cc index e4e0305..45acd48 100644 --- a/src/mutator.cc +++ b/src/mutator.cc @@ -15,9 +15,12 @@ #include "src/mutator.h" #include <algorithm> +#include <bitset> #include <map> +#include <memory> #include <random> #include <string> +#include <utility> #include <vector> #include "src/field_instance.h" @@ -26,6 +29,7 @@ namespace protobuf_mutator { +using google::protobuf::Any; using protobuf::Descriptor; using protobuf::FieldDescriptor; using protobuf::FileDescriptor; @@ -40,17 +44,22 @@ namespace { const int kMaxInitializeDepth = 200; const uint64_t kDefaultMutateWeight = 1000000; -enum class Mutation { +enum class Mutation : uint8_t { None, Add, // Adds new field with default value. Mutate, // Mutates field contents. Delete, // Deletes field. Copy, // Copy values copied from another field. + Clone, // Create new field with value copied from another. - // TODO(vitalybuka): - // Clone, // Adds new field with value copied from another field. + Last = Clone, }; +using MutationBitset = std::bitset<static_cast<size_t>(Mutation::Last)>; + +using Messages = std::vector<Message*>; +using ConstMessages = std::vector<const Message*>; + // Return random integer from [0, count) size_t GetRandomIndex(RandomEngine* random, size_t count) { assert(count > 0); @@ -124,14 +133,14 @@ class CanCopyAndDifferentField : public FieldFunction<CanCopyAndDifferentField, bool> { public: template <class T> - bool ForType(const ConstFieldInstance& src, - const ConstFieldInstance& dst) const { + bool ForType(const ConstFieldInstance& src, const ConstFieldInstance& dst, + int size_increase_hint) const { T s; src.Load(&s); if (!dst.CanStore(s)) return false; T d; dst.Load(&d); - return !IsEqual(s, d); + return SizeDiff(s, d) <= size_increase_hint && !IsEqual(s, d); } private: @@ -141,8 +150,8 @@ class CanCopyAndDifferentField return a.index == b.index; } - bool IsEqual(const std::unique_ptr<protobuf::Message>& a, - const std::unique_ptr<protobuf::Message>& b) const { + bool IsEqual(const std::unique_ptr<Message>& a, + const std::unique_ptr<Message>& b) const { return MessageDifferencer::Equals(*a, *b); } @@ -150,17 +159,31 @@ class CanCopyAndDifferentField bool IsEqual(const T& a, const T& b) const { return a == b; } + + int64_t SizeDiff(const std::unique_ptr<Message>& src, + const std::unique_ptr<Message>& dst) const { + return src->ByteSizeLong() - dst->ByteSizeLong(); + } + + int64_t SizeDiff(const std::string& src, const std::string& dst) const { + return src.size() - dst.size(); + } + + template <class T> + int64_t SizeDiff(const T&, const T&) const { + return 0; + } }; // Selects random field and mutation from the given proto message. class MutationSampler { public: - MutationSampler(bool keep_initialized, RandomEngine* random, Message* message) - : keep_initialized_(keep_initialized), random_(random), sampler_(random) { - Sample(message); - assert(mutation() != Mutation::None || - message->GetDescriptor()->field_count() == 0); - } + MutationSampler(bool keep_initialized, MutationBitset allowed_mutations, + RandomEngine* random) + : keep_initialized_(keep_initialized), + allowed_mutations_(allowed_mutations), + random_(random), + sampler_(random) {} // Returns selected field. const FieldInstance& field() const { return sampler_.selected().field; } @@ -168,8 +191,15 @@ class MutationSampler { // Returns selected mutation. Mutation mutation() const { return sampler_.selected().mutation; } - private: void Sample(Message* message) { + SampleImpl(message); + assert(mutation() != Mutation::None || + !allowed_mutations_[static_cast<size_t>(Mutation::Mutate)] || + message->GetDescriptor()->field_count() == 0); + } + + private: + void SampleImpl(Message* message) { const Descriptor* descriptor = message->GetDescriptor(); const Reflection* reflection = message->GetReflection(); @@ -186,59 +216,46 @@ class MutationSampler { const FieldDescriptor* add_field = oneof->field(GetRandomIndex(random_, oneof->field_count())); if (add_field != current_field) { - sampler_.Try(kDefaultMutateWeight, - {{message, add_field}, Mutation::Add}); + Try({message, add_field}, Mutation::Add); + Try({message, add_field}, Mutation::Clone); break; } if (oneof->field_count() < 2) break; } if (current_field) { - if (current_field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { - sampler_.Try(kDefaultMutateWeight, - {{message, current_field}, Mutation::Mutate}); - } - sampler_.Try(kDefaultMutateWeight, - {{message, current_field}, Mutation::Delete}); - sampler_.Try(kDefaultMutateWeight, - {{message, current_field}, Mutation::Copy}); + if (current_field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) + Try({message, current_field}, Mutation::Mutate); + Try({message, current_field}, Mutation::Delete); + Try({message, current_field}, Mutation::Copy); } } } else { if (field->is_repeated()) { int field_size = reflection->FieldSize(*message, field); - sampler_.Try( - kDefaultMutateWeight, - {{message, field, GetRandomIndex(random_, field_size + 1)}, - Mutation::Add}); + size_t random_index = GetRandomIndex(random_, field_size + 1); + Try({message, field, random_index}, Mutation::Add); + Try({message, field, random_index}, Mutation::Clone); if (field_size) { size_t random_index = GetRandomIndex(random_, field_size); - if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { - sampler_.Try(kDefaultMutateWeight, - {{message, field, random_index}, Mutation::Mutate}); - } - sampler_.Try(kDefaultMutateWeight, - {{message, field, random_index}, Mutation::Delete}); - sampler_.Try(kDefaultMutateWeight, - {{message, field, random_index}, Mutation::Copy}); + if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) + Try({message, field, random_index}, Mutation::Mutate); + Try({message, field, random_index}, Mutation::Delete); + Try({message, field, random_index}, Mutation::Copy); } } else { if (reflection->HasField(*message, field) || IsProto3SimpleField(*field)) { - if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { - sampler_.Try(kDefaultMutateWeight, - {{message, field}, Mutation::Mutate}); - } + if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) + Try({message, field}, Mutation::Mutate); if (!IsProto3SimpleField(*field) && (!field->is_required() || !keep_initialized_)) { - sampler_.Try(kDefaultMutateWeight, - {{message, field}, Mutation::Delete}); + Try({message, field}, Mutation::Delete); } - sampler_.Try(kDefaultMutateWeight, - {{message, field}, Mutation::Copy}); + Try({message, field}, Mutation::Copy); } else { - sampler_.Try(kDefaultMutateWeight, - {{message, field}, Mutation::Add}); + Try({message, field}, Mutation::Add); + Try({message, field}, Mutation::Clone); } } } @@ -247,15 +264,22 @@ class MutationSampler { if (field->is_repeated()) { const int field_size = reflection->FieldSize(*message, field); for (int j = 0; j < field_size; ++j) - Sample(reflection->MutableRepeatedMessage(message, field, j)); + SampleImpl(reflection->MutableRepeatedMessage(message, field, j)); } else if (reflection->HasField(*message, field)) { - Sample(reflection->MutableMessage(message, field)); + SampleImpl(reflection->MutableMessage(message, field)); } } } } + void Try(const FieldInstance& field, Mutation mutation) { + assert(mutation != Mutation::None); + if (!allowed_mutations_[static_cast<size_t>(mutation)]) return; + sampler_.Try(kDefaultMutateWeight, {field, mutation}); + } + bool keep_initialized_ = false; + MutationBitset allowed_mutations_; RandomEngine* random_; @@ -273,10 +297,13 @@ class MutationSampler { class DataSourceSampler { public: DataSourceSampler(const ConstFieldInstance& match, RandomEngine* random, - Message* message) - : match_(match), random_(random), sampler_(random) { - Sample(message); - } + int size_increase_hint) + : match_(match), + random_(random), + size_increase_hint_(size_increase_hint), + sampler_(random) {} + + void Sample(const Message& message) { SampleImpl(message); } // Returns selected field. const ConstFieldInstance& field() const { @@ -287,21 +314,21 @@ class DataSourceSampler { bool IsEmpty() const { return sampler_.IsEmpty(); } private: - void Sample(Message* message) { - const Descriptor* descriptor = message->GetDescriptor(); - const Reflection* reflection = message->GetReflection(); + void SampleImpl(const Message& message) { + const Descriptor* descriptor = message.GetDescriptor(); + const Reflection* reflection = message.GetReflection(); int field_count = descriptor->field_count(); for (int i = 0; i < field_count; ++i) { const FieldDescriptor* field = descriptor->field(i); if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { if (field->is_repeated()) { - const int field_size = reflection->FieldSize(*message, field); + const int field_size = reflection->FieldSize(message, field); for (int j = 0; j < field_size; ++j) { - Sample(reflection->MutableRepeatedMessage(message, field, j)); + SampleImpl(reflection->GetRepeatedMessage(message, field, j)); } - } else if (reflection->HasField(*message, field)) { - Sample(reflection->MutableMessage(message, field)); + } else if (reflection->HasField(message, field)) { + SampleImpl(reflection->GetMessage(message, field)); } } @@ -313,16 +340,16 @@ class DataSourceSampler { } if (field->is_repeated()) { - if (int field_size = reflection->FieldSize(*message, field)) { - ConstFieldInstance source(message, field, + if (int field_size = reflection->FieldSize(message, field)) { + ConstFieldInstance source(&message, field, GetRandomIndex(random_, field_size)); - if (CanCopyAndDifferentField()(source, match_)) + if (CanCopyAndDifferentField()(source, match_, size_increase_hint_)) sampler_.Try(field_size, source); } } else { - if (reflection->HasField(*message, field)) { - ConstFieldInstance source(message, field); - if (CanCopyAndDifferentField()(source, match_)) + if (reflection->HasField(message, field)) { + ConstFieldInstance source(&message, field); + if (CanCopyAndDifferentField()(source, match_, size_increase_hint_)) sampler_.Try(1, source); } } @@ -331,19 +358,162 @@ class DataSourceSampler { ConstFieldInstance match_; RandomEngine* random_; + int size_increase_hint_; WeightedReservoirSampler<ConstFieldInstance, RandomEngine> sampler_; }; +using UnpackedAny = + std::unordered_map<const Message*, std::unique_ptr<Message>>; + +const Descriptor* GetAnyTypeDescriptor(const Any& any) { + std::string type_name; + if (!Any::ParseAnyTypeUrl(std::string(any.type_url()), &type_name)) + return nullptr; + return any.descriptor()->file()->pool()->FindMessageTypeByName(type_name); +} + +std::unique_ptr<Message> UnpackAny(const Any& any) { + const Descriptor* desc = GetAnyTypeDescriptor(any); + if (!desc) return {}; + std::unique_ptr<Message> message( + any.GetReflection()->GetMessageFactory()->GetPrototype(desc)->New()); + message->ParsePartialFromString(std::string(any.value())); + return message; +} + +const Any* CastToAny(const Message* message) { + return Any::GetDescriptor() == message->GetDescriptor() + ? static_cast<const Any*>(message) + : nullptr; +} + +Any* CastToAny(Message* message) { + return Any::GetDescriptor() == message->GetDescriptor() + ? static_cast<Any*>(message) + : nullptr; +} + +std::unique_ptr<Message> UnpackIfAny(const Message& message) { + if (const Any* any = CastToAny(&message)) return UnpackAny(*any); + return {}; +} + +void UnpackAny(const Message& message, UnpackedAny* result) { + if (std::unique_ptr<Message> any = UnpackIfAny(message)) { + UnpackAny(*any, result); + result->emplace(&message, std::move(any)); + return; + } + + const Descriptor* descriptor = message.GetDescriptor(); + const Reflection* reflection = message.GetReflection(); + + for (int i = 0; i < descriptor->field_count(); ++i) { + const FieldDescriptor* field = descriptor->field(i); + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (field->is_repeated()) { + const int field_size = reflection->FieldSize(message, field); + for (int j = 0; j < field_size; ++j) { + UnpackAny(reflection->GetRepeatedMessage(message, field, j), result); + } + } else if (reflection->HasField(message, field)) { + UnpackAny(reflection->GetMessage(message, field), result); + } + } + } +} + +class PostProcessing { + public: + using PostProcessors = + std::unordered_multimap<const Descriptor*, Mutator::PostProcess>; + + PostProcessing(bool keep_initialized, const PostProcessors& post_processors, + const UnpackedAny& any, RandomEngine* random) + : keep_initialized_(keep_initialized), + post_processors_(post_processors), + any_(any), + random_(random) {} + + void Run(Message* message, int max_depth) { + --max_depth; + const Descriptor* descriptor = message->GetDescriptor(); + + // Apply custom mutators in nested messages before packing any. + const Reflection* reflection = message->GetReflection(); + for (int i = 0; i < descriptor->field_count(); i++) { + const FieldDescriptor* field = descriptor->field(i); + if (keep_initialized_ && + (field->is_required() || descriptor->options().map_entry()) && + !reflection->HasField(*message, field)) { + CreateDefaultField()(FieldInstance(message, field)); + } + + if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) continue; + + if (max_depth < 0 && !field->is_required()) { + // Clear deep optional fields to avoid stack overflow. + reflection->ClearField(message, field); + if (field->is_repeated()) + assert(!reflection->FieldSize(*message, field)); + else + assert(!reflection->HasField(*message, field)); + continue; + } + + if (field->is_repeated()) { + const int field_size = reflection->FieldSize(*message, field); + for (int j = 0; j < field_size; ++j) { + Message* nested_message = + reflection->MutableRepeatedMessage(message, field, j); + Run(nested_message, max_depth); + } + } else if (reflection->HasField(*message, field)) { + Message* nested_message = reflection->MutableMessage(message, field); + Run(nested_message, max_depth); + } + } + + if (Any* any = CastToAny(message)) { + if (max_depth < 0) { + // Clear deep Any fields to avoid stack overflow. + any->Clear(); + } else { + auto It = any_.find(message); + if (It != any_.end()) { + Run(It->second.get(), max_depth); + std::string value; + It->second->SerializePartialToString(&value); + *any->mutable_value() = value; + } + } + } + + // Call user callback after message trimmed, initialized and packed. + auto range = post_processors_.equal_range(descriptor); + for (auto it = range.first; it != range.second; ++it) + it->second(message, (*random_)()); + } + + private: + bool keep_initialized_; + const PostProcessors& post_processors_; + const UnpackedAny& any_; + RandomEngine* random_; +}; + } // namespace class FieldMutator { public: - FieldMutator(size_t size_increase_hint, bool enforce_changes, - bool enforce_utf8_strings, Mutator* mutator) + FieldMutator(int size_increase_hint, bool enforce_changes, + bool enforce_utf8_strings, const ConstMessages& sources, + Mutator* mutator) : size_increase_hint_(size_increase_hint), enforce_changes_(enforce_changes), enforce_utf8_strings_(enforce_utf8_strings), + sources_(sources), mutator_(mutator) {} void Mutate(int32_t* value) const { @@ -395,7 +565,8 @@ class FieldMutator { assert(*message); if (GetRandomBool(mutator_->random(), mutator_->random_to_default_ratio_)) return; - mutator_->MutateImpl(message->get(), size_increase_hint_); + mutator_->MutateImpl(sources_, {message->get()}, false, + size_increase_hint_); } private: @@ -412,9 +583,10 @@ class FieldMutator { } } - size_t size_increase_hint_; + int size_increase_hint_; size_t enforce_changes_; bool enforce_utf8_strings_; + const ConstMessages& sources_; Mutator* mutator_; }; @@ -422,11 +594,12 @@ namespace { struct MutateField : public FieldFunction<MutateField> { template <class T> - void ForType(const FieldInstance& field, size_t size_increase_hint, - Mutator* mutator) const { + void ForType(const FieldInstance& field, int size_increase_hint, + const ConstMessages& sources, Mutator* mutator) const { T value; field.Load(&value); - FieldMutator(size_increase_hint, true, field.EnforceUtf8(), mutator) + FieldMutator(size_increase_hint, true, field.EnforceUtf8(), sources, + mutator) .Mutate(&value); field.Store(value); } @@ -435,13 +608,13 @@ struct MutateField : public FieldFunction<MutateField> { struct CreateField : public FieldFunction<CreateField> { public: template <class T> - void ForType(const FieldInstance& field, size_t size_increase_hint, - Mutator* mutator) const { + void ForType(const FieldInstance& field, int size_increase_hint, + const ConstMessages& sources, Mutator* mutator) const { T value; field.GetDefault(&value); FieldMutator field_mutator(size_increase_hint, false /* defaults could be useful */, - field.EnforceUtf8(), mutator); + field.EnforceUtf8(), sources, mutator); field_mutator.Mutate(&value); field.Create(value); } @@ -451,210 +624,116 @@ struct CreateField : public FieldFunction<CreateField> { void Mutator::Seed(uint32_t value) { random_.seed(value); } -void Mutator::Mutate(Message* message, size_t size_increase_hint) { - MutateImpl(message, size_increase_hint); +void Mutator::Mutate(Message* message, size_t max_size_hint) { + UnpackedAny any; + UnpackAny(*message, &any); - InitializeAndTrim(message, kMaxInitializeDepth); - assert(!keep_initialized_ || message->IsInitialized()); + Messages messages; + messages.reserve(any.size() + 1); + messages.push_back(message); + for (const auto& kv : any) messages.push_back(kv.second.get()); - if (!post_processors_.empty()) { - ApplyPostProcessing(message); - } -} + ConstMessages sources(messages.begin(), messages.end()); + MutateImpl(sources, messages, false, + static_cast<int>(max_size_hint) - + static_cast<int>(message->ByteSizeLong())); -void Mutator::RegisterPostProcessor(const protobuf::Descriptor* desc, - PostProcess callback) { - post_processors_.emplace(desc, callback); + PostProcessing(keep_initialized_, post_processors_, any, &random_) + .Run(message, kMaxInitializeDepth); + assert(IsInitialized(*message)); } -void Mutator::ApplyPostProcessing(Message* message) { - const Descriptor* descriptor = message->GetDescriptor(); +void Mutator::CrossOver(const Message& message1, Message* message2, + size_t max_size_hint) { + UnpackedAny any; + UnpackAny(*message2, &any); - auto it = post_processors_.find(descriptor); - if (it != post_processors_.end()) { - it->second(message, random_()); - } + Messages messages; + messages.reserve(any.size() + 1); + messages.push_back(message2); + for (auto& kv : any) messages.push_back(kv.second.get()); - // Now recursively apply custom mutators. - const Reflection* reflection = message->GetReflection(); - for (int i = 0; i < descriptor->field_count(); i++) { - const FieldDescriptor* field = descriptor->field(i); - if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { - continue; - } - if (field->is_repeated()) { - const int field_size = reflection->FieldSize(*message, field); - for (int j = 0; j < field_size; ++j) { - Message* nested_message = - reflection->MutableRepeatedMessage(message, field, j); - ApplyPostProcessing(nested_message); - } - } else if (reflection->HasField(*message, field)) { - Message* nested_message = reflection->MutableMessage(message, field); - ApplyPostProcessing(nested_message); - } - } + UnpackAny(message1, &any); + + ConstMessages sources; + sources.reserve(any.size() + 2); + sources.push_back(&message1); + sources.push_back(message2); + for (const auto& kv : any) sources.push_back(kv.second.get()); + + MutateImpl(sources, messages, true, + static_cast<int>(max_size_hint) - + static_cast<int>(message2->ByteSizeLong())); + + PostProcessing(keep_initialized_, post_processors_, any, &random_) + .Run(message2, kMaxInitializeDepth); + assert(IsInitialized(*message2)); +} + +void Mutator::RegisterPostProcessor(const Descriptor* desc, + PostProcess callback) { + post_processors_.emplace(desc, callback); } -void Mutator::MutateImpl(Message* message, size_t size_increase_hint) { - for (;;) { - MutationSampler mutation(keep_initialized_, &random_, message); +bool Mutator::MutateImpl(const ConstMessages& sources, const Messages& messages, + bool copy_clone_only, int size_increase_hint) { + MutationBitset mutations; + if (copy_clone_only) { + mutations[static_cast<size_t>(Mutation::Copy)] = true; + mutations[static_cast<size_t>(Mutation::Clone)] = true; + } else if (size_increase_hint <= 16) { + mutations[static_cast<size_t>(Mutation::Delete)] = true; + } else { + mutations.set(); + mutations[static_cast<size_t>(Mutation::Copy)] = false; + mutations[static_cast<size_t>(Mutation::Clone)] = false; + } + while (mutations.any()) { + MutationSampler mutation(keep_initialized_, mutations, &random_); + for (Message* message : messages) mutation.Sample(message); + switch (mutation.mutation()) { case Mutation::None: - return; + return true; case Mutation::Add: - CreateField()(mutation.field(), size_increase_hint / 2, this); - return; + CreateField()(mutation.field(), size_increase_hint, sources, this); + return true; case Mutation::Mutate: - MutateField()(mutation.field(), size_increase_hint / 2, this); - return; + MutateField()(mutation.field(), size_increase_hint, sources, this); + return true; case Mutation::Delete: DeleteField()(mutation.field()); - return; + return true; + case Mutation::Clone: { + CreateDefaultField()(mutation.field()); + DataSourceSampler source_sampler(mutation.field(), &random_, + size_increase_hint); + for (const Message* source : sources) source_sampler.Sample(*source); + if (source_sampler.IsEmpty()) { + if (!IsProto3SimpleField(*mutation.field().descriptor())) + return true; // CreateField is enough for proto2. + break; + } + CopyField()(source_sampler.field(), mutation.field()); + return true; + } case Mutation::Copy: { - DataSourceSampler source(mutation.field(), &random_, message); - if (source.IsEmpty()) break; - CopyField()(source.field(), mutation.field()); - return; + DataSourceSampler source_sampler(mutation.field(), &random_, + size_increase_hint); + for (const Message* source : sources) source_sampler.Sample(*source); + if (source_sampler.IsEmpty()) break; + CopyField()(source_sampler.field(), mutation.field()); + return true; } default: assert(false && "unexpected mutation"); - return; + return false; } - } -} -void Mutator::CrossOver(const protobuf::Message& message1, - protobuf::Message* message2) { - // CrossOver can produce result which still equals to inputs. So we backup - // message2 to later comparison. message1 is already constant. - std::unique_ptr<protobuf::Message> message2_copy(message2->New()); - message2_copy->CopyFrom(*message2); - - CrossOverImpl(message1, message2); - - InitializeAndTrim(message2, kMaxInitializeDepth); - assert(!keep_initialized_ || message2->IsInitialized()); - - if (!post_processors_.empty()) { - ApplyPostProcessing(message2); - } - - // Can't call mutate from crossover because of a bug in libFuzzer. - // if (MessageDifferencer::Equals(*message2_copy, *message2) || - // MessageDifferencer::Equals(message1, *message2)) { - // Mutate(message2, 0); - // } -} - -void Mutator::CrossOverImpl(const protobuf::Message& message1, - protobuf::Message* message2) { - const Descriptor* descriptor = message2->GetDescriptor(); - const Reflection* reflection = message2->GetReflection(); - assert(message1.GetDescriptor() == descriptor); - assert(message1.GetReflection() == reflection); - - for (int i = 0; i < descriptor->field_count(); ++i) { - const FieldDescriptor* field = descriptor->field(i); - - if (field->is_repeated()) { - const int field_size1 = reflection->FieldSize(message1, field); - int field_size2 = reflection->FieldSize(*message2, field); - for (int j = 0; j < field_size1; ++j) { - ConstFieldInstance source(&message1, field, j); - FieldInstance destination(message2, field, field_size2++); - AppendField()(source, destination); - } - - assert(field_size2 == reflection->FieldSize(*message2, field)); - - // Shuffle - for (int j = 0; j < field_size2; ++j) { - if (int k = GetRandomIndex(&random_, field_size2 - j)) { - reflection->SwapElements(message2, field, j, j + k); - } - } - - int keep = GetRandomIndex(&random_, field_size2 + 1); - - if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - int remove = field_size2 - keep; - // Cross some message to keep with messages to remove. - int cross = GetRandomIndex(&random_, std::min(keep, remove) + 1); - for (int j = 0; j < cross; ++j) { - int k = GetRandomIndex(&random_, keep); - int r = keep + GetRandomIndex(&random_, remove); - assert(k != r); - CrossOverImpl(reflection->GetRepeatedMessage(*message2, field, r), - reflection->MutableRepeatedMessage(message2, field, k)); - } - } - - for (int j = keep; j < field_size2; ++j) - reflection->RemoveLast(message2, field); - assert(keep == reflection->FieldSize(*message2, field)); - - } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - if (!reflection->HasField(message1, field)) { - if (GetRandomBool(&random_)) - DeleteField()(FieldInstance(message2, field)); - } else if (!reflection->HasField(*message2, field)) { - if (GetRandomBool(&random_)) { - ConstFieldInstance source(&message1, field); - CopyField()(source, FieldInstance(message2, field)); - } - } else { - CrossOverImpl(reflection->GetMessage(message1, field), - reflection->MutableMessage(message2, field)); - } - } else { - if (GetRandomBool(&random_)) { - if (reflection->HasField(message1, field)) { - ConstFieldInstance source(&message1, field); - CopyField()(source, FieldInstance(message2, field)); - } else { - DeleteField()(FieldInstance(message2, field)); - } - } - } - } -} - -void Mutator::InitializeAndTrim(Message* message, int max_depth) { - const Descriptor* descriptor = message->GetDescriptor(); - const Reflection* reflection = message->GetReflection(); - for (int i = 0; i < descriptor->field_count(); ++i) { - const FieldDescriptor* field = descriptor->field(i); - if (keep_initialized_ && - (field->is_required() || descriptor->options().map_entry()) && - !reflection->HasField(*message, field)) { - CreateDefaultField()(FieldInstance(message, field)); - } - - if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - if (max_depth <= 0 && !field->is_required()) { - // Clear deep optional fields to avoid stack overflow. - reflection->ClearField(message, field); - if (field->is_repeated()) - assert(!reflection->FieldSize(*message, field)); - else - assert(!reflection->HasField(*message, field)); - continue; - } - - if (field->is_repeated()) { - const int field_size = reflection->FieldSize(*message, field); - for (int j = 0; j < field_size; ++j) { - Message* nested_message = - reflection->MutableRepeatedMessage(message, field, j); - InitializeAndTrim(nested_message, max_depth - 1); - } - } else if (reflection->HasField(*message, field)) { - Message* nested_message = reflection->MutableMessage(message, field); - InitializeAndTrim(nested_message, max_depth - 1); - } - } + // Don't try same mutation next time. + mutations[static_cast<size_t>(mutation.mutation())] = false; } + return false; } int32_t Mutator::MutateInt32(int32_t value) { return FlipBit(value, &random_); } @@ -681,14 +760,16 @@ size_t Mutator::MutateEnum(size_t index, size_t item_count) { } std::string Mutator::MutateString(const std::string& value, - size_t size_increase_hint) { + int size_increase_hint) { std::string result = value; while (!result.empty() && GetRandomBool(&random_)) { result.erase(GetRandomIndex(&random_, result.size()), 1); } - while (result.size() < size_increase_hint && GetRandomBool(&random_)) { + while (size_increase_hint > 0 && + result.size() < static_cast<size_t>(size_increase_hint) && + GetRandomBool(&random_)) { size_t index = GetRandomIndex(&random_, result.size() + 1); result.insert(result.begin() + index, GetRandomIndex(&random_, 1 << 8)); } @@ -706,10 +787,16 @@ std::string Mutator::MutateString(const std::string& value, } std::string Mutator::MutateUtf8String(const std::string& value, - size_t size_increase_hint) { + int size_increase_hint) { std::string str = MutateString(value, size_increase_hint); FixUtf8String(&str, &random_); return str; } +bool Mutator::IsInitialized(const Message& message) const { + if (!keep_initialized_ || message.IsInitialized()) return true; + std::cerr << "Uninitialized: " << message.DebugString() << "\n"; + return false; +} + } // namespace protobuf_mutator |