Page 1 of 1

UnknownMemberException thrown on local variable assignment

PostPosted: Wed Apr 23, 2014 2:08 am
by NoshBar
Hi,

I came across Cobra just last week and thought it looked very interesting, especially liking the different ensure/require/test/body sections.
The language seems pretty easy to understand too, or so I think.
Having finally given up on Visual Studio yesterday, I went for Xamarin Studio which is a time-saver bar-none with the auto-completion (even if it has now stopped giving me real-time debug information for some reason).

However, I'm stuck on something incredibly stupid, and I'm feeling quite silly and lazy posting this, but I was wondering if anyone could help with the simplest of (bad) code segments there is (searches on this forum and Google reveal nothing).

I have a function which assigns a local variable the value of a class-level array element value.
Upon running this I get an UnknownMemberException pointing to the variable called "byte" (changing the name and/or type seems to make no difference).

"""
cobra --version
Cobra 0.9.6 on .NET CLR v4.0.30319 on Microsoft Windows NT 6.1.7601 Service Pack 1
"""

class Loader
var contents
var offset as int

def readByte as int
require
.contents <> nil
body
byte = .contents[.offset] # this is where Xamarin Studio points when the exception is thrown
.offset = .offset + 1
return byte

def load(filename as String)
.offset = 0
.contents = File.readAllBytes(filename)

if .readByte <> 65 # if it doesn't equal 'A', we have a problem
print "Invalid header\n"

class Program
def main
loader as Loader? = Loader()
loader.load('test.txt') # simply contains ABCDE
print "Done.\n"


The full exception report is here: https://dl.dropboxusercontent.com/u/4716604/cobra-exception-report.html
Plain exception text:
Code: Select all
Unhandled Exception: Cobra.Core.UnknownMemberException: obj=Byte[][65 (Byte), 66 (Byte), 67 (Byte), 68 (Byte), 69 (Byte)], name='[]', type=System.Byte[]
   at Cobra.Core.CobraImp.GetIndexerValue(Object obj, Object[] args)
   at Loader.ReadByte() in d:\noshbar\cobra_problem_1\Program.cobra:line 16
   at Loader.Load(String filename) in d:\noshbar\cobra_problem_1\Program.cobra:line 23
   at Program.Main() in d:\noshbar\cobra_problem_1\Program.cobra:line 29


Help and humiliation are appreciated, thanks!

Re: UnknownMemberException thrown on local variable assignme

PostPosted: Wed Apr 23, 2014 11:05 am
by nerdzero
Hmm, looks like a bug related to dynamic typing. You can workaround it until it's fixed by adding an explicit type to .contents or by using a non-dynamic temporary variable.

class Loader
var contents as uint8[]?
...

or
body
contents = .contents to uint8[] # the type will be inferred via the cast operation
byte = contents[.offset]
...


Either of these should circumvent the exception that is getting thrown by Cobra.Core.CobraImp.GetIndexerValue

Re: UnknownMemberException thrown on local variable assignme

PostPosted: Thu Apr 24, 2014 12:06 am
by NoshBar
Hey, thank you so much for the reply!

I had tried:
class Loader
var contents as uint8[]

... but then got an error when I checked it against nil in the "require" section.
Adding the question mark has naturally fixed that, silly me.

Thank you again for the help, especially on something so trivial!

Re: UnknownMemberException thrown on local variable assignme

PostPosted: Thu Apr 24, 2014 4:40 am
by hopscc
Caught between a dynamic rock and a hard place....

More detail than you care about....
Its throwing the Exception from the runtime library call implementing the indexer ( getIndexerValue) because that support method doesnt implement indexing on an array (only Collections) ;)
It probably should at least try for single rank arrays since so much of .Net provides arrays ( of bytes or ints or strings) rather than Lists which is what cobra 'prefers' you to use...
Once past that ( I hacked an array lookup into getIndexerValue ) Theres then an issue with getBytes returning an int from an array of bytes declared as dynamic...

A workaround is to declare types a little more as nerdzero suggested or make the .contents array a list on read....but you then have to explicitly convert the byte read to an int for return
def readByte as int
require
.contents <> nil
body
byte = .contents[.offset] # no Exception now
.offset = .offset + 1
#return byte # <<- Invalid cast Exception cos byte is actually dynamic/Object
return Convert.toInt32(byte)

def load(filename as String)
.offset = 0
.contents = File.readAllBytes(filename).toList # store as a collection/List
if .readByte <> 65 # if it doesn't equal 'A', we have a problem
print "Invalid header\n"


... dcl uint8[] ... but then got an error when I checked it against nil in the "require" section.


Since its no longer a nilable reference you now need check against the array length being non zero. But you would then get an error for it
being nil and nonnilable ( i.e not being initialised )when the (invisible) initialiser/constructor completes :)

In such cases is probably better to just initialise it to something innocuous and temporary of the desired type so all the internal type and nil checking is kept quiet.

This was all (?) needed to change to make it happy

class Loader
var contents = @[0u8] # initialise to a one element array of uint8
var offset as int


def readByte as int
require
.contents.length # consequent to the initialisation above


The weird looking punctuation after the '=' ( @[ 0u8 ] )
is a literal for an array (@[ ) containing a single element literal, value 0 (0) typed as an unsigned int of 8 bits (u8)

Good find for some missing cobra functionality - I'll ticket it with what I've tweaked so far...

Re: UnknownMemberException thrown on local variable assignme

PostPosted: Thu Apr 24, 2014 8:47 am
by NoshBar
What an absolutely marvellous reply, thank you!
And on the contrary, the more information in a reply, the better.

I'm converting some hacky C code I made a while into Cobra, hence me doing it "wrong":
  • I had no idea you could convert an array to a list so easily, so thanks for pointing out .toList
  • I realise a function called readByte returning an int is somewhat silly, but I was struggling with types in my initial conversion, so I'll clean that up once it's all compiling, but good to know about Convert.toInt32 too.

Perhaps this is cheeky to ask in the same thread, but I was wondering how to properly convert a sub-set (start index -> start index + length) of the uint8[] array into a String.
The array didn't appear to have a slice() function, and the copyTo() function didn't have promising parameters, so I simply ended up making a local array, copying the sub-range over, then calling System.Text.Encoding.default.getString(localContents), which just feels dirty.

Then again, if I'm turning it into a list, I'm even more lost. I'm sorry I'm abusing things, I'll get better!

Re: UnknownMemberException thrown on local variable assignme

PostPosted: Thu Apr 24, 2014 12:39 pm
by kirai84
NoshBar wrote: how to properly convert a sub-set (start index -> start index + length) of the uint8[] array into a String.

There is a System.Text.Encoding.default.getString(bytes, index, count) method.

Re: UnknownMemberException thrown on local variable assignme

PostPosted: Fri Apr 25, 2014 4:16 am
by hopscc
or you could use cobras slicing to get the subset then use whatever you would use to convert an a block of bytes to a String of the desired encoding
byteSubrange = .contents[0:4]  # first 3 bytes (start:end+1)
string = System.Text.Encoding.utf8.getString(byteSubrange)
# print String # 'ABCD'

Re: UnknownMemberException thrown on local variable assignme

PostPosted: Fri May 02, 2014 4:48 am
by NoshBar
Aha, awesome. Thanks for the pointers, both of you, I really appreciate the help (especially when I'm being so stupid).