Testing for empty dictionaries

So, the following code

if (data.isEmpty()) {

gives:
Error: Array Out Of Bounds Error

when it's "Lang.Dictionary {size: 0}"

Similarly,

if (data.size() == 0) {

Also gave the same error.

So what can I do to test its size so it doesn't crash?!?

  • OMG, I compiled with Monkey C 1.0.10 and it works. What the *** is wrong with this 1.0.11 Compiler. Anybody using it?!?

  • 1.0.10 and 1.0.11 are versions of the VS Code extension, right? But the compiler is packaged with the SDK, not the VS Code extension, and has a version number which matches the SDK version. You can easily verify this by:

    - looking at the compiler output in the terminal, which shows that monkeybrains.jar (the compiler) is located in the SDK folder

    - adding "-v" to the compiler options, and noting that the compiler version is the same as the SDK version

    Also note that it's entirely possible to build and simulate CIQ apps without even opening VS Code, so it would be very strange if the VS Code extension was somehow directly causing this problem.

    I tried building the following code in VS Code and running it in the simulator, and it didn't crash:

    var d = {};
    System.println("d.isEmpty() = " + d.isEmpty()); // prints "true"

    - Devices: edge1050, fr955, fr935

    - OS: Windows 11

    - VS Code Monkey C extension: 1.0.11 (the latest)

    Could provide more details about how to recreate this problem, please? e.g. it would help if you specified: full code fragment which demonstrates the problem (the code provided doesn't show how data is constructed), SDK version, devices tested, whether you tested in simulator or on real devices, and compiler optimization level.

  • I can't paste the source code. The site says:

    Sorry, you have been blocked
    You are unable to access forums.garmin.com

    If I try by simply pasting the code here, I get the error

    An error occurred. Please try again or contact your administrator.

    But you can see it here: https://github.com/SylvainGa/crystal-face/tree/With-OWM

    It's in the source/CrystalApp,mc file, starting at line 96 (onBackgroundData)

    SDK is the latest, 7.4.3.

    I'm simulating a Fenix 7S Pro.

    The monkeyC.optimizationLevel is set to Default.

    This is the line it ran to compile

    Executing task: C:\Program Files (x86)\Common Files\Oracle\Java\javapath\java.exe -Xms1g -Dfile.encoding=UTF-8 -Dapple.awt.UIElement=true -jar c:\Users\Sylvain\AppData\Roaming\Garmin\ConnectIQ\Sdks\connectiq-sdk-win-7.4.3-2024-12-11-90ec25e45\bin\monkeybrains.jar -o bin\crystalface.prg -f c:\Users\Sylvain\git\crystal-face\monkey.jungle -y C:\Users\Sylvain\Documents\Garmin\developer_key -d fenix7spro_sim -w -l 0

    I also had a weird issue with 1.0.11 regarding the 'has' keyword. See here

    https://forums.garmin.com/developer/connect-iq/f/discussion/398300/has-timetorecovery-is-true-for-the-venu2-plus-and-crashes-because-well-it-s-not-there

    I was able to work around it but I shouldn't have to do that,

  • The forums are messed up but you can usually post a screenshot or a link to pastebin.

    Here's an excerpt of the source code you mentioned:

    function onBackgroundData(data) {
    ...
            var type = data.keys()[0]; // Type of received data.

    Not to state the obvious, but the code on github doesn't have any reference to data.isEmpty() or data.size(). I assume that's additional guard code that you added to prevent a crash on the line with data.keys()[0]?

    We really need to see the code that *you* are testing with. (Sorry if I'm missing something here.)

    I don't think the following line is a good idea in any case:

    var type = data.keys()[0]; // Type of received data.

    You can't assume the order of keys in a dictionary. Even if keys are sometimes (or always) returned in the order that they're added today, you can't assume it will be the case tomorrow, since the order of keys in a dictionary isn't guaranteed as far as I know.

    Why wouldn't you simply refer to the type key by name?

    I also had a weird issue with 1.0.11 regarding the 'has' keyword. See here

    As stated above, the compiler is associated with the SDK, not the extension.

    You can see for yourself from the command line that you pasted:

    Executing task: C:\Program Files (x86)\Common Files\Oracle\Java\javapath\java.exe -Xms1g -Dfile.encoding=UTF-8 -Dapple.awt.UIElement=true -jar c:\Users\Sylvain\AppData\Roaming\Garmin\ConnectIQ\Sdks\connectiq-sdk-win-7.4.3-2024-12-11-90ec25e45\bin\monkeybrains.jar -o bin\crystalface.prg -f c:\Users\Sylvain\git\crystal-face\monkey.jungle -y C:\Users\Sylvain\Documents\Garmin\developer_key -d fenix7spro_sim -w -l 0

    The simulator is also associated with the SDK and not the extension.

    The only way I can see the extension making a difference is if 1.0.10 generates different command lines for the compiler or simulator than 1.0.11.

    Can you reproduce this with a *simple* example (not a fully fledged app)? With respect, it's not feasible for people on the forums to test this problem with a background process that makes a bunch of webrequests to OWM and Tesla, especially if they don't have the exact code that you're testing with.

    Does the same behaviour happen on a real device?

  • Oups, I forgot to push to the repo. It's done now. I did a simple app with the code you posted (https://github.com/SylvainGa/TestDict/tree/main) and it works with the compiler V1.0.10 and 1.0.11 through SDK 7.4.3. I then switched back to my Crystal-Tesla app and recompiled with V1.0.11 and it now works. I don't know what to make of this, beside a bug somewhere...

    PS. The main reason why I never used the 1.0.11 compiler in the first place is still present (for me at least). Somehow, I need to run 'Clean Project' with that version before recompiling since it ignores the change I made in the source files and simply runs the last compiled version. Very annoying.

  • I'm happy to hear that it's working for you now.

    I feel like you're completely ignoring what I said about the compiler not being associated with the VS Code extension (versions 1.0.10 and 1.0.11), but with the SDK (version 7.4.3). Again, you don't even need to use VS Code to build CIQ projects.

    The bug / annoying behavior where the project is not rebuilt when it should be does sound like it would be associated with the extension (as presumably it's the extension which decides whether to call the compiler or not in the first place, when you use the Run command in VS Code).

    To be fair, the extension is known for being buggy / flaky, and I think what you're seeing is a legit complaint.

    PS. The main reason why I never used the 1.0.11 compiler in the first place is still present (for me at least). Somehow, I need to run 'Clean Project' with that version before recompiling since it ignores the change I made in the source files and simply runs the last compiled version. Very annoying.

    I think this is a clue as to what might have actually happened.

    Here's a scenario which could explain what you saw.

    1) You started with this code (and no isEmpty() test):

    var type = data.keys()[0]; // Type of received data.

    2) You then added your isEmpty() test before that line:

    if (data.isEmpty()) {
      /*DEBUG*/ logMessage("onBackgroundData:Empty data");
      return;
    }

    3) You ran the project in the simulator using VS Code, but the extension didn't recompile your code with the new change, so the app crashed with the following error:

    Error: Array Out Of Bounds Error

    4) You assumed that the error was happening on the line with if (data.isEmpty()), but imo it's much more likely that the error was happening on the line with var type = data.keys()[0].

    Again, I wouldn't recommend accessing data.keys()[0] in the first place, as your code seems to assume that that the 0 index of the dictionary's keys will be the first key that you added to the dictionary. I don't think this is a safe assumption to make. I still don't see why you can't simply access the key by name.

  • TL;DR the simplest explanation is that the sim was incorrectly running the earlier version of code and crashing in a place that makes sense (data.keys()[0]) as opposed to crashing in a place that doesn't make sense (data.isEmpty()), especially given the specific error message (array out of bounds).

    Indeed, you reported that your project was not being rebuilt when it should be, which fits the scenario I outlined.

  • The var type = data.keys()[0]; was from the original developer. I assume he was expecting just one web request data to be returned at a time but I now piggy back two of them together. I'm now iterating through the data keys to fetch all the data. It's working well now.

  • In your first post, have you tried doing a null check on data before the data.isEmpty() or data.size()==0?

  • No, because according to the doc, it's

    data as Application.PersistableType

    and not

    data as Application.PersistableType or Null

    However, you can't never be too safe, so I just added  a null check too.

    In my first post, it was returning an empty dictionary, not null. The empty dictionary isn't a state that my code should send in the first place. When I do a Background.exit, it should always return something, either the data if the return code is 200 or the error code itself if it's something else. I fixed it but wanted to make sure it doesn't crash if it happens again for some reason.