| // Copyright 2016 Google Inc. All rights reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "src/mutator.h" | |
| #include <algorithm> | |
| #include <bitset> | |
| #include <iostream> | |
| #include <map> | |
| #include <memory> | |
| #include <random> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "src/field_instance.h" | |
| #include "src/utf8_fix.h" | |
| #include "src/weighted_reservoir_sampler.h" | |
| namespace protobuf_mutator { | |
| using google::protobuf::Any; | |
| using protobuf::Descriptor; | |
| using protobuf::FieldDescriptor; | |
| using protobuf::FileDescriptor; | |
| using protobuf::Message; | |
| using protobuf::OneofDescriptor; | |
| using protobuf::Reflection; | |
| using protobuf::util::MessageDifferencer; | |
| using std::placeholders::_1; | |
| namespace { | |
| const int kMaxInitializeDepth = 200; | |
| const uint64_t kDefaultMutateWeight = 1000000; | |
| 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. | |
| Last = Clone, | |
| }; | |
| using MutationBitset = std::bitset<static_cast<size_t>(Mutation::Last) + 1>; | |
| 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); | |
| if (count == 1) return 0; | |
| return std::uniform_int_distribution<size_t>(0, count - 1)(*random); | |
| } | |
| // Flips random bit in the buffer. | |
| void FlipBit(size_t size, uint8_t* bytes, RandomEngine* random) { | |
| size_t bit = GetRandomIndex(random, size * 8); | |
| bytes[bit / 8] ^= (1u << (bit % 8)); | |
| } | |
| // Flips random bit in the value. | |
| template <class T> | |
| T FlipBit(T value, RandomEngine* random) { | |
| FlipBit(sizeof(value), reinterpret_cast<uint8_t*>(&value), random); | |
| return value; | |
| } | |
| // Return true with probability about 1-of-n. | |
| bool GetRandomBool(RandomEngine* random, size_t n = 2) { | |
| return GetRandomIndex(random, n) == 0; | |
| } | |
| bool IsProto3SimpleField(const FieldDescriptor& field) { | |
| assert(field.file()->syntax() == FileDescriptor::SYNTAX_PROTO3 || | |
| field.file()->syntax() == FileDescriptor::SYNTAX_PROTO2); | |
| return field.file()->syntax() == FileDescriptor::SYNTAX_PROTO3 && | |
| field.cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE && | |
| !field.containing_oneof() && !field.is_repeated(); | |
| } | |
| struct CreateDefaultField : public FieldFunction<CreateDefaultField> { | |
| template <class T> | |
| void ForType(const FieldInstance& field) const { | |
| T value; | |
| field.GetDefault(&value); | |
| field.Create(value); | |
| } | |
| }; | |
| struct DeleteField : public FieldFunction<DeleteField> { | |
| template <class T> | |
| void ForType(const FieldInstance& field) const { | |
| field.Delete(); | |
| } | |
| }; | |
| struct CopyField : public FieldFunction<CopyField> { | |
| template <class T> | |
| void ForType(const ConstFieldInstance& source, | |
| const FieldInstance& field) const { | |
| T value; | |
| source.Load(&value); | |
| field.Store(value); | |
| } | |
| }; | |
| struct AppendField : public FieldFunction<AppendField> { | |
| template <class T> | |
| void ForType(const ConstFieldInstance& source, | |
| const FieldInstance& field) const { | |
| T value; | |
| source.Load(&value); | |
| field.Create(value); | |
| } | |
| }; | |
| class CanCopyAndDifferentField | |
| : public FieldFunction<CanCopyAndDifferentField, bool> { | |
| public: | |
| template <class T> | |
| 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 SizeDiff(s, d) <= size_increase_hint && !IsEqual(s, d); | |
| } | |
| private: | |
| bool IsEqual(const ConstFieldInstance::Enum& a, | |
| const ConstFieldInstance::Enum& b) const { | |
| assert(a.count == b.count); | |
| return a.index == b.index; | |
| } | |
| bool IsEqual(const std::unique_ptr<Message>& a, | |
| const std::unique_ptr<Message>& b) const { | |
| return MessageDifferencer::Equals(*a, *b); | |
| } | |
| template <class T> | |
| 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, 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; } | |
| // Returns selected mutation. | |
| Mutation mutation() const { return sampler_.selected().mutation; } | |
| 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(); | |
| int field_count = descriptor->field_count(); | |
| for (int i = 0; i < field_count; ++i) { | |
| const FieldDescriptor* field = descriptor->field(i); | |
| if (const OneofDescriptor* oneof = field->containing_oneof()) { | |
| // Handle entire oneof group on the first field. | |
| if (field->index_in_oneof() == 0) { | |
| assert(oneof->field_count()); | |
| const FieldDescriptor* current_field = | |
| reflection->GetOneofFieldDescriptor(*message, oneof); | |
| for (;;) { | |
| const FieldDescriptor* add_field = | |
| oneof->field(GetRandomIndex(random_, oneof->field_count())); | |
| if (add_field != current_field) { | |
| 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) | |
| 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); | |
| 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) | |
| 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) | |
| Try({message, field}, Mutation::Mutate); | |
| if (!IsProto3SimpleField(*field) && | |
| (!field->is_required() || !keep_initialized_)) { | |
| Try({message, field}, Mutation::Delete); | |
| } | |
| Try({message, field}, Mutation::Copy); | |
| } else { | |
| Try({message, field}, Mutation::Add); | |
| Try({message, field}, Mutation::Clone); | |
| } | |
| } | |
| } | |
| 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) | |
| SampleImpl(reflection->MutableRepeatedMessage(message, field, j)); | |
| } else if (reflection->HasField(*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_; | |
| struct Result { | |
| Result() = default; | |
| Result(const FieldInstance& f, Mutation m) : field(f), mutation(m) {} | |
| FieldInstance field; | |
| Mutation mutation = Mutation::None; | |
| }; | |
| WeightedReservoirSampler<Result, RandomEngine> sampler_; | |
| }; | |
| // Selects random field of compatible type to use for clone mutations. | |
| class DataSourceSampler { | |
| public: | |
| DataSourceSampler(const ConstFieldInstance& match, RandomEngine* random, | |
| 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 { | |
| assert(!IsEmpty()); | |
| return sampler_.selected(); | |
| } | |
| bool IsEmpty() const { return sampler_.IsEmpty(); } | |
| private: | |
| 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); | |
| for (int j = 0; j < field_size; ++j) { | |
| SampleImpl(reflection->GetRepeatedMessage(message, field, j)); | |
| } | |
| } else if (reflection->HasField(message, field)) { | |
| SampleImpl(reflection->GetMessage(message, field)); | |
| } | |
| } | |
| if (field->cpp_type() != match_.cpp_type()) continue; | |
| if (match_.cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { | |
| if (field->enum_type() != match_.enum_type()) continue; | |
| } else if (match_.cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { | |
| if (field->message_type() != match_.message_type()) continue; | |
| } | |
| if (field->is_repeated()) { | |
| if (int field_size = reflection->FieldSize(message, field)) { | |
| ConstFieldInstance source(&message, field, | |
| GetRandomIndex(random_, field_size)); | |
| 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_, size_increase_hint_)) | |
| sampler_.Try(1, source); | |
| } | |
| } | |
| } | |
| } | |
| 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(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 { | |
| RepeatMutate(value, std::bind(&Mutator::MutateInt32, mutator_, _1)); | |
| } | |
| void Mutate(int64_t* value) const { | |
| RepeatMutate(value, std::bind(&Mutator::MutateInt64, mutator_, _1)); | |
| } | |
| void Mutate(uint32_t* value) const { | |
| RepeatMutate(value, std::bind(&Mutator::MutateUInt32, mutator_, _1)); | |
| } | |
| void Mutate(uint64_t* value) const { | |
| RepeatMutate(value, std::bind(&Mutator::MutateUInt64, mutator_, _1)); | |
| } | |
| void Mutate(float* value) const { | |
| RepeatMutate(value, std::bind(&Mutator::MutateFloat, mutator_, _1)); | |
| } | |
| void Mutate(double* value) const { | |
| RepeatMutate(value, std::bind(&Mutator::MutateDouble, mutator_, _1)); | |
| } | |
| void Mutate(bool* value) const { | |
| RepeatMutate(value, std::bind(&Mutator::MutateBool, mutator_, _1)); | |
| } | |
| void Mutate(FieldInstance::Enum* value) const { | |
| RepeatMutate(&value->index, | |
| std::bind(&Mutator::MutateEnum, mutator_, _1, value->count)); | |
| assert(value->index < value->count); | |
| } | |
| void Mutate(std::string* value) const { | |
| if (enforce_utf8_strings_) { | |
| RepeatMutate(value, std::bind(&Mutator::MutateUtf8String, mutator_, _1, | |
| size_increase_hint_)); | |
| } else { | |
| RepeatMutate(value, std::bind(&Mutator::MutateString, mutator_, _1, | |
| size_increase_hint_)); | |
| } | |
| } | |
| void Mutate(std::unique_ptr<Message>* message) const { | |
| assert(!enforce_changes_); | |
| assert(*message); | |
| if (GetRandomBool(mutator_->random(), mutator_->random_to_default_ratio_)) | |
| return; | |
| mutator_->MutateImpl(sources_, {message->get()}, false, | |
| size_increase_hint_); | |
| } | |
| private: | |
| template <class T, class F> | |
| void RepeatMutate(T* value, F mutate) const { | |
| if (!enforce_changes_ && | |
| GetRandomBool(mutator_->random(), mutator_->random_to_default_ratio_)) { | |
| return; | |
| } | |
| T tmp = *value; | |
| for (int i = 0; i < 10; ++i) { | |
| *value = mutate(*value); | |
| if (!enforce_changes_ || *value != tmp) return; | |
| } | |
| } | |
| int size_increase_hint_; | |
| size_t enforce_changes_; | |
| bool enforce_utf8_strings_; | |
| const ConstMessages& sources_; | |
| Mutator* mutator_; | |
| }; | |
| namespace { | |
| struct MutateField : public FieldFunction<MutateField> { | |
| template <class T> | |
| 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(), sources, | |
| mutator) | |
| .Mutate(&value); | |
| field.Store(value); | |
| } | |
| }; | |
| struct CreateField : public FieldFunction<CreateField> { | |
| public: | |
| template <class T> | |
| 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(), sources, mutator); | |
| field_mutator.Mutate(&value); | |
| field.Create(value); | |
| } | |
| }; | |
| } // namespace | |
| void Mutator::Seed(uint32_t value) { random_.seed(value); } | |
| void Mutator::Fix(Message* message) { | |
| UnpackedAny any; | |
| UnpackAny(*message, &any); | |
| PostProcessing(keep_initialized_, post_processors_, any, &random_) | |
| .Run(message, kMaxInitializeDepth); | |
| assert(IsInitialized(*message)); | |
| } | |
| void Mutator::Mutate(Message* message, size_t max_size_hint) { | |
| UnpackedAny any; | |
| UnpackAny(*message, &any); | |
| Messages messages; | |
| messages.reserve(any.size() + 1); | |
| messages.push_back(message); | |
| for (const auto& kv : any) messages.push_back(kv.second.get()); | |
| ConstMessages sources(messages.begin(), messages.end()); | |
| MutateImpl(sources, messages, false, | |
| static_cast<int>(max_size_hint) - | |
| static_cast<int>(message->ByteSizeLong())); | |
| PostProcessing(keep_initialized_, post_processors_, any, &random_) | |
| .Run(message, kMaxInitializeDepth); | |
| assert(IsInitialized(*message)); | |
| } | |
| void Mutator::CrossOver(const Message& message1, Message* message2, | |
| size_t max_size_hint) { | |
| UnpackedAny any; | |
| UnpackAny(*message2, &any); | |
| Messages messages; | |
| messages.reserve(any.size() + 1); | |
| messages.push_back(message2); | |
| for (auto& kv : any) messages.push_back(kv.second.get()); | |
| 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); | |
| } | |
| 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 true; | |
| case Mutation::Add: | |
| CreateField()(mutation.field(), size_increase_hint, sources, this); | |
| return true; | |
| case Mutation::Mutate: | |
| MutateField()(mutation.field(), size_increase_hint, sources, this); | |
| return true; | |
| case Mutation::Delete: | |
| DeleteField()(mutation.field()); | |
| 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_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 false; | |
| } | |
| // 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_); } | |
| int64_t Mutator::MutateInt64(int64_t value) { return FlipBit(value, &random_); } | |
| uint32_t Mutator::MutateUInt32(uint32_t value) { | |
| return FlipBit(value, &random_); | |
| } | |
| uint64_t Mutator::MutateUInt64(uint64_t value) { | |
| return FlipBit(value, &random_); | |
| } | |
| float Mutator::MutateFloat(float value) { return FlipBit(value, &random_); } | |
| double Mutator::MutateDouble(double value) { return FlipBit(value, &random_); } | |
| bool Mutator::MutateBool(bool value) { return !value; } | |
| size_t Mutator::MutateEnum(size_t index, size_t item_count) { | |
| if (item_count <= 1) return 0; | |
| return (index + 1 + GetRandomIndex(&random_, item_count - 1)) % item_count; | |
| } | |
| std::string Mutator::MutateString(const std::string& value, | |
| int size_increase_hint) { | |
| std::string result = value; | |
| while (!result.empty() && GetRandomBool(&random_)) { | |
| result.erase(GetRandomIndex(&random_, result.size()), 1); | |
| } | |
| 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)); | |
| } | |
| if (result != value) return result; | |
| if (result.empty()) { | |
| result.push_back(GetRandomIndex(&random_, 1 << 8)); | |
| return result; | |
| } | |
| if (!result.empty()) | |
| FlipBit(result.size(), reinterpret_cast<uint8_t*>(&result[0]), &random_); | |
| return result; | |
| } | |
| std::string Mutator::MutateUtf8String(const std::string& value, | |
| 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 |