kaitai-io / kaitai_struct

Kaitai Struct: declarative language to generate binary data parsers in C++ / C# / Go / Java / JavaScript / Lua / Nim / Perl / PHP / Python / Ruby
https://kaitai.io
3.96k stars 192 forks source link

C# compiled classes don't use enum to_i casting #802

Open kjczarne opened 4 years ago

kjczarne commented 4 years ago

Hi again, seems like I've spotted another bug. Correct me if I'm wrong ;)

Given the following KSY snippet:

- id: payload
  type:
    switch-on: _parent.msg_id
    cases:
      'some_enum::set_a.to_i': type_a
      'some_enum::set_b.to_i': type_b

I get the following in C#:

public KaitaiStruct Payload
            {
                get
                {
                    if (f_payload)
                        return _payload;
                    switch (M_Parent.MsgId) {
                    case Packet.SomeEnum.SetA: {
                        _payload = new TypeA(m_io, this, m_root);
                        break;
                    }
                    case Packet.SomeEnum.SetB: {
                        _payload = new TypeB(m_io, this, m_root);
                        break;
                    }
                    f_payload = true;
                    return _payload;
                }
            }

I get the following errors:

error CS0266: Cannot implicitly convert type 'Kaitai.Packet.SomeEnum.SetA' to 'byte'.
error CS0266: Cannot implicitly convert type 'Kaitai.Packet.SomeEnum.SetB' to 'byte'.

To me, it seems like the to_i cast is missing from the switch cases and should look something like this:

public KaitaiStruct Payload
            {
                get
                {
                    if (f_payload)
                        return _payload;
                    switch (M_Parent.MsgId) {
                    case ((int)Packet.SomeEnum.SetA): {
                        _payload = new TypeA(m_io, this, m_root);
                        break;
                    }
                    case ((int)Packet.SomeEnum.SetB): {
                        _payload = new TypeB(m_io, this, m_root);
                        break;
                    }
                    f_payload = true;
                    return _payload;
                }
            }
kjczarne commented 4 years ago

Any news on this one? Am I misunderstanding something conceptually?

generalmimon commented 4 years ago

@kjczarne Thanks for reporting! I found out that a simple comparison of an enum value converted to integer using to_i and an integer literal is enough to reveal the bug:

meta:
  id: csharp_enum_to_i
instances:
  enum_eq_int:
    value: 'fruit::apple.to_i == 2'
enums:
  fruit:
    2: apple

This translates to the following C# code:

_enumEqInt = (bool) (Fruit.Apple == 2);

which fails in the C# compiler with this error:

error CS0019: Operator `==' cannot be applied to operands of type `Kaitai.CsharpEnumToI.Fruit' and `int'

This points to the enumToInt method implementation in CSharpTranslator of the compiler, which apparently should do the typecasting:

  override def enumToInt(v: expr, et: EnumType): String =
    translate(v)
kjczarne commented 4 years ago

Gonna keep an eye on this one. Would submit a PR but my Scala skills are non-existent :(

Jmerk523 commented 2 years ago

Has anyone tried doing the typecasting directly as a workaround? E.g.: instead of fruit::apple.to_i == 2 try fruit::apple.as<u4> == 2 or, if the above doesn't work, perhaps using both will trick it into casting the enum's value to something else: fruit::apple.to_i.as<u4> == 2 This works as a workaround for me, though I'm using code from master so I am not sure if this is available in the release code.

kjczarne commented 2 years ago

I'll give it a try this weekend ;)