From 47eddf8d571a6cb292d945bc0a9ec8c5b67aae29 Mon Sep 17 00:00:00 2001 From: Oskari Timperi Date: Fri, 27 Apr 2018 19:23:10 +0300 Subject: Generate object variants for oneofs This fixes a segfault problem that I had, when running the conformance test suite. The following piece of code segfaulted on the last assignment: msg.oneofUint32 = 1 msg.oneofString = "test" If the first assignment was `0`, then there was no problem. Now we use the object variant, which gives nim runtime the ability to track what data the union holds. --- nimpb/compiler/generator.nim | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/nimpb/compiler/generator.nim b/nimpb/compiler/generator.nim index 809788e..452e49e 100644 --- a/nimpb/compiler/generator.nim +++ b/nimpb/compiler/generator.nim @@ -659,16 +659,22 @@ iterator genType(message: Message): string = for oneof in message.oneofs: yield "" - yield &"{message.names}_{oneof.name}_OneOf* {{.union.}} = object" + yield indent(&"{message.names}_{oneof.name}_Kind* {{.pure.}} = enum", 0) for field in oneof.fields: - yield indent(&"{quoteReserved(field.name)}: {field.fullType}", 4) + yield indent(&"{field.name.capitalizeAscii}", 4) + yield "" + yield &"{message.names}_{oneof.name}_OneOf* = object" + yield indent(&"case kind*: {message.names}_{oneof.name}_Kind", 4) + for field in oneof.fields: + yield indent(&"of {message.names}_{oneof.name}_Kind.{field.name.capitalizeAscii}: {quoteReserved(field.name)}*: {field.fullType}", 4) iterator genNewMessageProc(msg: Message): string = yield &"proc new{msg.names}*(): {msg.names} =" yield indent("new(result)", 4) yield indent("initMessage(result[])", 4) for field in msg.fields: - yield indent(&"result.{field.accessor} = {defaultValue(field)}", 4) + if field.oneof == nil: + yield indent(&"result.{field.accessor} = {defaultValue(field)}", 4) yield "" iterator oneofSiblings(field: Field): Field = @@ -680,7 +686,10 @@ iterator oneofSiblings(field: Field): Field = iterator genClearFieldProc(msg: Message, field: Field): string = yield &"proc clear{field.name}*(message: {msg.names}) =" - yield indent(&"message.{field.accessor} = {defaultValue(field)}", 4) + if field.oneof == nil: + yield indent(&"message.{field.accessor} = {defaultValue(field)}", 4) + else: + yield indent(&"reset(message.{field.oneof.name})", 4) var numbers: seq[int] = @[field.number] for sibling in oneofSiblings(field): add(numbers, sibling.number) @@ -697,7 +706,13 @@ iterator genHasFieldProc(msg: Message, field: Field): string = iterator genSetFieldProc(msg: Message, field: Field): string = yield &"proc set{field.name}*(message: {msg.names}, value: {field.fullType}) =" - yield indent(&"message.{field.accessor} = value", 4) + if field.oneof == nil: + yield indent(&"message.{field.accessor} = value", 4) + else: + yield indent(&"if message.{field.oneof.name}.kind != {msg.names}_{field.oneof.name}_Kind.{field.name.capitalizeAscii}:", 4) + yield indent(&"reset(message.{field.oneof.name})", 8) + yield indent(&"message.{field.oneof.name}.kind = {msg.names}_{field.oneof.name}_Kind.{field.name.capitalizeAscii}", 8) + yield indent(&"message.{field.accessor} = value", 4) yield indent(&"setField(message, {field.number})", 4) var numbers: seq[int] = @[] for sibling in oneofSiblings(field): -- cgit v1.2.3