// // This set of handlers can write into a proto2::Message whose reflection class // is _pi::Proto2Reflection (ie. proto1 messages; while slightly confusing, the // name "Proto2Reflection" indicates that it is a reflection class implementing // the proto2 reflection interface, but is used for proto1 generated messages). // // Like FieldAccessor this depends on breaking encapsulation, and will need to // be changed if and when the details of _pi::Proto2Reflection change. // // Note that we have received an exception from c-style-artiters regarding // dynamic_cast<> in this file: // https://groups.google.com/a/google.com/d/msg/c-style/7Zp_XCX0e7s/I6dpzno4l-MJ #include "upb/bindings/googlepb/proto1.int.h" #include // TEMPORARY measure until we update the friend declarations in proto1. // Can't do in a single CL because of components. #define private public #define protected public #include "net/proto2/public/repeated_field.h" #include "net/proto/internal_layout.h" #include "net/proto/proto2_reflection.h" #undef private #undef protected #ifdef GOOGLE_PROTOBUF_HAS_ARENAS namespace proto2 { class Arena; } #endif #include "upb/def.h" #include "upb/handlers.h" #include "upb/shim/shim.h" #include "upb/sink.h" // Unconditionally evaluate, but also assert in debug mode. #define CHKRET(x) do { bool ok = (x); UPB_ASSERT(ok); } while (0) template static T* GetPointer(void* message, size_t offset) { return reinterpret_cast(static_cast(message) + offset); } namespace upb { namespace googlepb { class P2R_Handlers { public: // Returns true if we were able to set an accessor and any other properties // of the FieldDef that are necessary to read/write this field to a // proto2::Message. static bool TrySet(const proto2::FieldDescriptor* proto2_f, const proto2::Message& m, const upb::FieldDef* upb_f, upb::Handlers* h) { const proto2::Reflection* base_r = m.GetReflection(); // See file comment re: dynamic_cast. const _pi::Proto2Reflection* r = dynamic_cast(base_r); if (!r) return false; // Extensions don't exist in proto1. UPB_ASSERT(!proto2_f->is_extension()); #define PRIMITIVE(name, type_name) \ case _pi::CREP_REQUIRED_##name: \ case _pi::CREP_OPTIONAL_##name: \ case _pi::CREP_REPEATED_##name: \ SetPrimitiveHandlers(proto2_f, r, upb_f, h); \ return true; switch (r->GetFieldLayout(proto2_f)->crep) { PRIMITIVE(DOUBLE, double); PRIMITIVE(FLOAT, float); PRIMITIVE(INT64, int64_t); PRIMITIVE(UINT64, uint64_t); PRIMITIVE(INT32, int32_t); PRIMITIVE(FIXED64, uint64_t); PRIMITIVE(FIXED32, uint32_t); PRIMITIVE(BOOL, bool); case _pi::CREP_REQUIRED_STRING: case _pi::CREP_OPTIONAL_STRING: case _pi::CREP_REPEATED_STRING: SetStringHandlers(proto2_f, r, upb_f, h); return true; case _pi::CREP_OPTIONAL_OUTOFLINE_STRING: SetOutOfLineStringHandlers(proto2_f, r, upb_f, h); return true; case _pi::CREP_REQUIRED_CORD: case _pi::CREP_OPTIONAL_CORD: case _pi::CREP_REPEATED_CORD: SetCordHandlers(proto2_f, r, upb_f, h); return true; case _pi::CREP_REQUIRED_GROUP: case _pi::CREP_REQUIRED_FOREIGN: case _pi::CREP_REQUIRED_FOREIGN_PROTO2: SetRequiredMessageHandlers(proto2_f, m, r, upb_f, h); return true; case _pi::CREP_OPTIONAL_GROUP: case _pi::CREP_REPEATED_GROUP: case _pi::CREP_OPTIONAL_FOREIGN: case _pi::CREP_REPEATED_FOREIGN: case _pi::CREP_OPTIONAL_FOREIGN_PROTO2: case _pi::CREP_REPEATED_FOREIGN_PROTO2: SetMessageHandlers(proto2_f, m, r, upb_f, h); return true; case _pi::CREP_OPTIONAL_FOREIGN_WEAK: case _pi::CREP_OPTIONAL_FOREIGN_WEAK_PROTO2: SetWeakMessageHandlers(proto2_f, m, r, upb_f, h); return true; default: UPB_ASSERT(false); return false; } } #undef PRIMITIVE // If the field "f" in the message "m" is a weak field, returns the prototype // of the submessage (which may be a specific type or may be OpaqueMessage). // Otherwise returns NULL. static const proto2::Message* GetWeakPrototype( const proto2::Message& m, const proto2::FieldDescriptor* f) { // See file comment re: dynamic_cast. const _pi::Proto2Reflection* r = dynamic_cast(m.GetReflection()); if (!r) return NULL; const _pi::Field* field = r->GetFieldLayout(f); if (field->crep == _pi::CREP_OPTIONAL_FOREIGN_WEAK) { return static_cast( field->weak_layout()->default_instance); } else if (field->crep == _pi::CREP_OPTIONAL_FOREIGN_WEAK_PROTO2) { return field->proto2_weak_default_instance(); } else { return NULL; } } // If "m" is a message that uses Proto2Reflection, returns the prototype of // the submessage (which may be OpaqueMessage for a weak field that is not // linked in). Otherwise returns NULL. static const proto2::Message* GetFieldPrototype( const proto2::Message& m, const proto2::FieldDescriptor* f) { // See file comment re: dynamic_cast. const proto2::Message* ret = GetWeakPrototype(m, f); if (ret) { return ret; } else if (dynamic_cast(m.GetReflection())) { // Since proto1 has no dynamic message, it must be from the generated // factory. UPB_ASSERT(f->cpp_type() == proto2::FieldDescriptor::CPPTYPE_MESSAGE); ret = proto2::MessageFactory::generated_factory()->GetPrototype( f->message_type()); UPB_ASSERT(ret); return ret; } else { return NULL; } } private: class FieldOffset { public: FieldOffset(const proto2::FieldDescriptor* f, const _pi::Proto2Reflection* r) : offset_(GetOffset(f, r)), is_repeated_(f->is_repeated()) { if (!is_repeated_) { int64_t hasbit = GetHasbit(f, r); hasbyte_ = hasbit / 8; mask_ = 1 << (hasbit % 8); } } template T* GetFieldPointer(proto2::Message* message) const { return GetPointer(message, offset_); } void SetHasbit(void* message) const { UPB_ASSERT(!is_repeated_); uint8_t* byte = GetPointer(message, hasbyte_); *byte |= mask_; } private: const size_t offset_; bool is_repeated_; // Only for non-repeated fields. int32_t hasbyte_; int8_t mask_; }; static upb_selector_t GetSelector(const upb::FieldDef* f, upb::Handlers::Type type) { upb::Handlers::Selector selector; bool ok = upb::Handlers::GetSelector(f, type, &selector); UPB_ASSERT(ok); return selector; } static int16_t GetHasbit(const proto2::FieldDescriptor* f, const _pi::Proto2Reflection* r) { UPB_ASSERT(!f->is_repeated()); return (r->layout_->has_bit_offset * 8) + r->GetFieldLayout(f)->has_index; } static uint16_t GetOffset(const proto2::FieldDescriptor* f, const _pi::Proto2Reflection* r) { return r->GetFieldLayout(f)->offset; } // StartSequence ///////////////////////////////////////////////////////////// template static void SetStartRepeatedField( const proto2::FieldDescriptor* proto2_f, const _pi::Proto2Reflection* r, const upb::FieldDef* f, upb::Handlers* h) { CHKRET(h->SetStartSequenceHandler( f, UpbBindT(PushOffset >, new FieldOffset(proto2_f, r)))); } template static void SetStartRepeatedPtrField( const proto2::FieldDescriptor* proto2_f, const _pi::Proto2Reflection* r, const upb::FieldDef* f, upb::Handlers* h) { CHKRET(h->SetStartSequenceHandler( f, UpbBindT(PushOffset >, new FieldOffset(proto2_f, r)))); } static void SetStartRepeatedSubmessageField( const proto2::FieldDescriptor* proto2_f, const _pi::Proto2Reflection* r, const upb::FieldDef* f, upb::Handlers* h) { CHKRET(h->SetStartSequenceHandler( f, UpbBind(PushOffset, new FieldOffset(proto2_f, r)))); } template static T* PushOffset(proto2::Message* m, const FieldOffset* offset) { return offset->GetFieldPointer(m); } // Primitive Value (numeric, enum, bool) ///////////////////////////////////// template static void SetPrimitiveHandlers(const proto2::FieldDescriptor* proto2_f, const _pi::Proto2Reflection* r, const upb::FieldDef* f, upb::Handlers* h) { if (f->IsSequence()) { SetStartRepeatedField(proto2_f, r, f, h); CHKRET(h->SetValueHandler(f, UpbMakeHandlerT(Append))); } else { CHKRET( upb::Shim::Set(h, f, GetOffset(proto2_f, r), GetHasbit(proto2_f, r))); } } template static void Append(proto2::RepeatedField* r, T val) { // Proto1's ProtoArray class derives from proto2::RepeatedField. r->Add(val); } // String //////////////////////////////////////////////////////////////////// static void SetStringHandlers(const proto2::FieldDescriptor* proto2_f, const _pi::Proto2Reflection* r, const upb::FieldDef* f, upb::Handlers* h) { h->SetStringHandler(f, UpbMakeHandler(OnStringBuf)); if (f->IsSequence()) { SetStartRepeatedPtrField(proto2_f, r, f, h); CHKRET(h->SetStartStringHandler(f, UpbMakeHandler(StartRepeatedString))); } else { CHKRET(h->SetStartStringHandler( f, UpbBind(StartString, new FieldOffset(proto2_f, r)))); } } static string* StartString(proto2::Message* m, const FieldOffset* info, size_t size_hint) { info->SetHasbit(m); string* str = info->GetFieldPointer(m); str->clear(); // reserve() here appears to hurt performance rather than help. return str; } static void OnStringBuf(string* s, const char* buf, size_t n) { s->append(buf, n); } static string* StartRepeatedString(proto2::RepeatedPtrField* r, size_t size_hint) { string* str = r->Add(); // reserve() here appears to hurt performance rather than help. return str; } // Out-of-line string //////////////////////////////////////////////////////// static void SetOutOfLineStringHandlers( const proto2::FieldDescriptor* proto2_f, const _pi::Proto2Reflection* r, const upb::FieldDef* f, upb::Handlers* h) { // This type is only used for non-repeated string fields. UPB_ASSERT(!f->IsSequence()); CHKRET(h->SetStartStringHandler( f, UpbBind(StartOutOfLineString, new FieldOffset(proto2_f, r)))); CHKRET(h->SetStringHandler(f, UpbMakeHandler(OnStringBuf))); } static string* StartOutOfLineString(proto2::Message* m, const FieldOffset* info, size_t size_hint) { info->SetHasbit(m); string** str = info->GetFieldPointer(m); if (*str == &::proto2::internal::GetEmptyString()) *str = new string(); (*str)->clear(); // reserve() here appears to hurt performance rather than help. return *str; } // Cord ////////////////////////////////////////////////////////////////////// static void SetCordHandlers(const proto2::FieldDescriptor* proto2_f, const _pi::Proto2Reflection* r, const upb::FieldDef* f, upb::Handlers* h) { if (f->IsSequence()) { SetStartRepeatedField(proto2_f, r, f, h); CHKRET(h->SetStartStringHandler(f, UpbMakeHandler(StartRepeatedCord))); } else { CHKRET(h->SetStartStringHandler( f, UpbBind(StartCord, new FieldOffset(proto2_f, r)))); } CHKRET(h->SetStringHandler(f, UpbMakeHandler(OnCordBuf))); } static Cord* StartCord(proto2::Message* m, const FieldOffset* offset, size_t size_hint) { UPB_UNUSED(size_hint); offset->SetHasbit(m); Cord* field = offset->GetFieldPointer(m); field->Clear(); return field; } static void OnCordBuf(Cord* c, const char* buf, size_t n) { c->Append(StringPiece(buf, n)); } static Cord* StartRepeatedCord(proto2::RepeatedField* r, size_t size_hint) { UPB_UNUSED(size_hint); return r->Add(); } // SubMessage //////////////////////////////////////////////////////////////// class SubMessageHandlerData : public FieldOffset { public: SubMessageHandlerData(const proto2::Message& prototype, const proto2::FieldDescriptor* f, const _pi::Proto2Reflection* r) : FieldOffset(f, r) { prototype_ = GetWeakPrototype(prototype, f); if (!prototype_) prototype_ = GetFieldPrototype(prototype, f); } const proto2::Message* prototype() const { return prototype_; } private: const proto2::Message* prototype_; }; static void SetRequiredMessageHandlers( const proto2::FieldDescriptor* proto2_f, const proto2::Message& m, const _pi::Proto2Reflection* r, const upb::FieldDef* f, upb::Handlers* h) { if (f->IsSequence()) { SetStartRepeatedSubmessageField(proto2_f, r, f, h); CHKRET(h->SetStartSubMessageHandler( f, UpbBind(StartRepeatedSubMessage, new SubMessageHandlerData(m, proto2_f, r)))); } else { CHKRET(h->SetStartSubMessageHandler( f, UpbBind(StartRequiredSubMessage, new FieldOffset(proto2_f, r)))); } } static proto2::Message* StartRequiredSubMessage(proto2::Message* m, const FieldOffset* offset) { offset->SetHasbit(m); return offset->GetFieldPointer(m); } static void SetMessageHandlers(const proto2::FieldDescriptor* proto2_f, const proto2::Message& m, const _pi::Proto2Reflection* r, const upb::FieldDef* f, upb::Handlers* h) { std::unique_ptr data( new SubMessageHandlerData(m, proto2_f, r)); if (f->IsSequence()) { SetStartRepeatedSubmessageField(proto2_f, r, f, h); CHKRET(h->SetStartSubMessageHandler( f, UpbBind(StartRepeatedSubMessage, data.release()))); } else { CHKRET(h->SetStartSubMessageHandler( f, UpbBind(StartSubMessage, data.release()))); } } static void SetWeakMessageHandlers(const proto2::FieldDescriptor* proto2_f, const proto2::Message& m, const _pi::Proto2Reflection* r, const upb::FieldDef* f, upb::Handlers* h) { std::unique_ptr data( new SubMessageHandlerData(m, proto2_f, r)); if (f->IsSequence()) { SetStartRepeatedSubmessageField(proto2_f, r, f, h); CHKRET(h->SetStartSubMessageHandler( f, UpbBind(StartRepeatedSubMessage, data.release()))); } else { CHKRET(h->SetStartSubMessageHandler( f, UpbBind(StartWeakSubMessage, data.release()))); } } static void* StartSubMessage(proto2::Message* m, const SubMessageHandlerData* info) { info->SetHasbit(m); proto2::Message** subm = info->GetFieldPointer(m); if (*subm == info->prototype()) *subm = (*subm)->New(); return *subm; } static void* StartWeakSubMessage(proto2::Message* m, const SubMessageHandlerData* info) { info->SetHasbit(m); proto2::Message** subm = info->GetFieldPointer(m); if (*subm == NULL) { *subm = info->prototype()->New(); } return *subm; } class RepeatedMessageTypeHandler { public: typedef proto2::Message Type; #ifndef GOOGLE_PROTOBUF_HAS_ARENAS // AddAllocated() calls this, but only if other objects are sitting // around waiting for reuse, which we will not do. static void Delete(Type* t) { UPB_UNUSED(t); UPB_ASSERT(false); } #else static ::proto2::Arena* GetArena(Type* t) { return t->GetArena(); } static void* GetMaybeArenaPointer(Type* t) { return t->GetMaybeArenaPointer(); } static inline Type* NewFromPrototype( const Type* prototype, ::proto2::Arena* arena = NULL) { return prototype->New(arena); } // AddAllocated() calls this, but only if other objects are sitting // around waiting for reuse, which we will not do. static void Delete(Type* t, ::proto2::Arena* arena) { UPB_UNUSED(t); UPB_UNUSED(arena); UPB_ASSERT(false); } static void Merge(const Type& from, Type* to) { to->MergeFrom(from); } #endif }; // Closure is a RepeatedPtrField*, but we access it through // its base class RepeatedPtrFieldBase*. static proto2::Message* StartRepeatedSubMessage( proto2::internal::RepeatedPtrFieldBase* r, const SubMessageHandlerData* info) { proto2::Message* submsg = r->AddFromCleared(); if (!submsg) { submsg = info->prototype()->New(); r->AddAllocated(submsg); } return submsg; } }; bool TrySetProto1WriteHandlers(const proto2::FieldDescriptor* proto2_f, const proto2::Message& m, const upb::FieldDef* upb_f, upb::Handlers* h) { return googlepb::P2R_Handlers::TrySet(proto2_f, m, upb_f, h); } const proto2::Message* GetProto1FieldPrototype( const proto2::Message& m, const proto2::FieldDescriptor* f) { const proto2::Message *weak = googlepb::P2R_Handlers::GetWeakPrototype(m, f); if (weak) return weak; if (f->cpp_type() != proto2::FieldDescriptor::CPPTYPE_MESSAGE) { return NULL; } return googlepb::P2R_Handlers::GetFieldPrototype(m, f); } } // namespace googlepb } // namespace upb