-
Notifications
You must be signed in to change notification settings - Fork 489
[table-driven-branch] Deinitialize and copy submessage fields. #1869
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[table-driven-branch] Deinitialize and copy submessage fields. #1869
Conversation
In order to perform the low-level typed operations on the raw memory, the protobuf runtime needs to be able to refer statically to the type of a submessage field. This is inherently challenging when we're trying to use a single storage class to represent all possible messages. To achieve this, we generate trampoline functions that are passed into the `_MessageLayout` initializer and stored. Then, for example, when deinitializing a submessage field, the storage class calls the deinitializer trampoline with the appropriate submessage index and the trampoline function immediately calls back into the storage class but passes it the type hint so that the memory can be bound to the correct type. Likewise for copying a submessage field. This replaces the original notion of a submessage accessor function that relied on returning the metatype as an existential value. That wouldn't be compatible with embedded Swift; these type hints should be. I've also updated the generator to avoid generating these trampoline functions if a message doesn't have any submessages. In this case, we can just pass a placeholder function baked into the runtime that does a precondition failure, so we save a bit on codegen.
The new tests verify submessages (singular and repeated), including CoW behavior.
static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}optional_int32\0\u{3}optional_int64\0\u{3}optional_uint32\0\u{3}optional_uint64\0\u{3}optional_sint32\0\u{3}optional_sint64\0\u{3}optional_fixed32\0\u{3}optional_fixed64\0\u{3}optional_sfixed32\0\u{3}optional_sfixed64\0\u{3}optional_float\0\u{3}optional_double\0\u{3}optional_bool\0\u{3}optional_string\0\u{3}optional_bytes\0\u{7}OptionalGroup\0\u{4}\u{2}optional_nested_message\0\u{3}optional_foreign_message\0\u{3}optional_import_message\0\u{3}optional_nested_enum\0\u{3}optional_foreign_enum\0\u{3}optional_import_enum\0\u{4}\u{3}optional_public_import_message\0\u{4}\u{5}repeated_int32\0\u{3}repeated_int64\0\u{3}repeated_uint32\0\u{3}repeated_uint64\0\u{3}repeated_sint32\0\u{3}repeated_sint64\0\u{3}repeated_fixed32\0\u{3}repeated_fixed64\0\u{3}repeated_sfixed32\0\u{3}repeated_sfixed64\0\u{3}repeated_float\0\u{3}repeated_double\0\u{3}repeated_bool\0\u{3}repeated_string\0\u{3}repeated_bytes\0\u{7}RepeatedGroup\0\u{4}\u{2}repeated_nested_message\0\u{3}repeated_foreign_message\0\u{3}repeated_import_message\0\u{3}repeated_nested_enum\0\u{3}repeated_foreign_enum\0\u{3}repeated_import_enum\0\u{4}\u{8}default_int32\0\u{3}default_int64\0\u{3}default_uint32\0\u{3}default_uint64\0\u{3}default_sint32\0\u{3}default_sint64\0\u{3}default_fixed32\0\u{3}default_fixed64\0\u{3}default_sfixed32\0\u{3}default_sfixed64\0\u{3}default_float\0\u{3}default_double\0\u{3}default_bool\0\u{3}default_string\0\u{3}default_bytes\0\u{4}\u{6}default_nested_enum\0\u{3}default_foreign_enum\0\u{3}default_import_enum\0\u{4}\u{1c}oneof_uint32\0\u{3}oneof_nested_message\0\u{3}oneof_string\0\u{3}oneof_bytes\0\u{b}something_old\0\u{b}reserved_field\0\u{b}something_long_gone\0\u{c}JIt\u{3}\u{1}\u{c}LIt\u{3}\u{1}\u{c}lHt\u{3}\u{a}\u{c}~Ht\u{3}\u{2}\u{c}KIt\u{3}\u{1}") | ||
#if _pointerBitWidth(_64) | ||
static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout("\0\u{10}\u{4}\0C\0\0\0\0\0\u{11}\0\0\u{1}\0\0\0\0 \0\0\0\0\0\0\u{5}\u{2}\0\0\0\0p\0\0\u{1}\0\0\0\u{3}\u{3}\0\0\0\0$\0\0\u{2}\0\0\0\u{d}\u{4}\0\0\0\0x\0\0\u{3}\0\0\0\u{4}\u{5}\0\0\0\0(\0\0\u{4}\0\0\0\u{11}\u{6}\0\0\0\0\0\u{1}\0\u{5}\0\0\0\u{12}\u{7}\0\0\0\0,\0\0\u{6}\0\0\0\u{7}\u{8}\0\0\0\0\u{8}\u{1}\0\u{7}\0\0\0\u{6}\u{9}\0\0\0\00\0\0\u{8}\0\0\0\u{f}\u{a}\0\0\0\0\u{10}\u{1}\0\u{9}\0\0\0\u{10}\u{b}\0\0\0\04\0\0\u{a}\0\0\0\u{2}\u{c}\0\0\0\0\u{18}\u{1}\0\u{b}\0\0\0\u{1}\u{d}\0\0\0\0\u{1c}\0\0\u{c}\0\0\0\u{8}\u{e}\0\0\0\00\u{3}\0\u{d}\0\0\0\u{9}\u{f}\0\0\0\0@\u{3}\0\u{e}\0\0\0\u{c}\u{10}\0\0\0\0P\u{1}\0\u{f}\0\0\0\u{a}\u{12}\0\0\0\0X\u{1}\0\u{10}\0\0\0\u{b}\u{13}\0\0\0\0`\u{1}\0\u{11}\0\0\0\u{b}\u{14}\0\0\0\0h\u{1}\0\u{12}\0\0\0\u{b}\u{15}\0\0\0\08\0\0\u{13}\0\0\0\u{e}\u{16}\0\0\0\0<\0\0\u{14}\0\0\0\u{e}\u{17}\0\0\0\0@\0\0\u{15}\0\0\0\u{e}\u{1a}\0\0\0\0p\u{1}\0\u{16}\0\0\0\u{b}\u{1f}\0\0\0\u{2}x\u{1}\0\u{17}\0\0\0\u{5} \0\0\0\u{2}\0\u{2}\0\u{18}\0\0\0\u{3}!\0\0\0\u{2}\u{8}\u{2}\0\u{19}\0\0\0\u{d}\"\0\0\0\u{2}\u{10}\u{2}\0\u{1a}\0\0\0\u{4}#\0\0\0\u{2}\u{18}\u{2}\0\u{1b}\0\0\0\u{11}$\0\0\0\u{2} \u{2}\0\u{1c}\0\0\0\u{12}%\0\0\0\u{2}(\u{2}\0\u{1d}\0\0\0\u{7}&\0\0\0\u{2}0\u{2}\0\u{1e}\0\0\0\u{6}'\0\0\0\u{2}8\u{2}\0\u{1f}\0\0\0\u{f}(\0\0\0\u{2}@\u{2}\0 \0\0\0\u{10})\0\0\0\u{2}H\u{2}\0!\0\0\0\u{2}*\0\0\0\u{2}P\u{2}\0\"\0\0\0\u{1}+\0\0\0\u{2}X\u{2}\0#\0\0\0\u{8},\0\0\0\u{2}`\u{2}\0$\0\0\0\u{9}-\0\0\0\u{2}h\u{2}\0%\0\0\0\u{c}.\0\0\0\u{2}p\u{2}\0&\0\0\0\u{a}0\0\0\0\u{2}x\u{2}\0'\0\0\0\u{b}1\0\0\0\u{2}\0\u{3}\0(\0\0\0\u{b}2\0\0\0\u{2}\u{8}\u{3}\0)\0\0\0\u{b}3\0\0\0\u{2}\u{10}\u{3}\0*\0\0\0\u{e}4\0\0\0\u{2}\u{18}\u{3}\0+\0\0\0\u{e}5\0\0\0\u{2} \u{3}\0,\0\0\0\u{e}=\0\0\0\0D\0\0-\0\0\0\u{5}>\0\0\0\0 \u{1}\0.\0\0\0\u{3}?\0\0\0\0H\0\0/\0\0\0\u{d}@\0\0\0\0(\u{1}\00\0\0\0\u{4}A\0\0\0\0L\0\01\0\0\0\u{11}B\0\0\0\00\u{1}\02\0\0\0\u{12}C\0\0\0\0P\0\03\0\0\0\u{7}D\0\0\0\08\u{1}\04\0\0\0\u{6}E\0\0\0\0T\0\05\0\0\0\u{f}F\0\0\0\0@\u{1}\06\0\0\0\u{10}G\0\0\0\0X\0\07\0\0\0\u{2}H\0\0\0\0H\u{1}\08\0\0\0\u{1}I\0\0\0\0\u{1d}\0\09\0\0\0\u{8}J\0\0\0\0P\u{3}\0:\0\0\0\u{9}K\0\0\0\0`\u{3}\0;\0\0\0\u{c}Q\0\0\0\0\\\0\0<\0\0\0\u{e}R\0\0\0\0`\0\0=\0\0\0\u{e}S\0\0\0\0d\0\0>\0\0\0\u{e}o\0\0\0\0h\0\0s\u{7f}\0\0\u{d}p\0\0\0\0(\u{3}\0s\u{7f}\0\0\u{b}q\0\0\0\0p\u{3}\0s\u{7f}\0\0\u{9}r\0\0\0\0\0\u{4}\0s\u{7f}\0\0\u{c}") | ||
@_alwaysEmitIntoClient @inline(__always) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why the directives here since this is already in the generated code? Does it force it into the init call?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So as part of my deep diving into codegen, if this was just a regular property, we'd still generate a separate accessor for it. That accessor serves no purpose, because the string is only referenced from one place. @_alwaysEmitIntoClient
ensures that the symbol for the entry point is never generated.
Now that the _MessageLayout
initializer can take other functions, I didn't want to duplicate all of that in each branch of the #if
. So this change lets us break out the layout string into a separate property and make that be the only part that's conditionally compiled, and then the _MessageLayout
initializer just references the symbol at no cost because it will be emitted directly at the call site.
let submessageNames = messageLayoutCalculator.submessageNames | ||
let deinitializeSubmessageName: String | ||
let copySubmessageName: String | ||
if submessageNames.isEmpty { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if it would save anything, but you could put two initializers, one with hooks and one without, and then have the runtime carry the one without the hooks that just defaults the arguments. it might even let you make those default private within the runtime so theres a few less symbols to expose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. That makes the generator a lot cleaner and it also means we don't have to expose those entry points outside of the runtime.
Merging this since I have |
In order to perform the low-level typed operations on the raw memory, the protobuf runtime needs to be able to refer statically to the type of a submessage field. This is inherently challenging when we're trying to use a single storage class to represent all possible messages.
To achieve this, we generate trampoline functions that are passed into the
_MessageLayout
initializer and stored. Then, for example, when deinitializing a submessage field, the storage class calls the deinitializer trampoline with the appropriate submessage index and the trampoline function immediately calls back into the storage class but passes it the type hint so that the memory can be bound to the correct type. Likewise for copying a submessage field.This replaces the original notion of a submessage accessor function that relied on returning the metatype as an existential value. That wouldn't be compatible with embedded Swift; these type hints should be.
I've also updated the generator to avoid generating these trampoline functions if a message doesn't have any submessages. In this case, we can just pass a placeholder function baked into the runtime that does a precondition failure, so we save a bit on codegen.