Big update to prettier-extension-monkeyc

I've posted about prettier-extension-monkeyc before, but I've added a bunch of new features that developers will probably like (well, I've been missing them, so maybe you have too).

The new features it implements for VSCode include:

  • Goto Definition. Point at a symbol, Ctrl/Cmd click, and it will take you to the definition. Or F12
  • Goto References. Right click on a symbol and select "Goto References". It will show you all the references. Or Shift-F12
  • Peek Definition/Peek References. Same as above, but in a popup window so you don't lose your place in the original document.
  • Rename Symbol. Right click on a local, function, class or module name, and select "Rename Symbol". It will rename all the references. It doesn't yet work for class members/methods.
  • Goto Symbol. Type Ctrl/Cmd-Shift-O and pick a symbol from the drop down (which has a hierarchical view of all symbols in the current file). This also appears as an outline across the top of the file.
  • Open Symbol By Name. Type Ctrl/Cmd-T, then start typing letters from a symbol name. A drop down will be populated with all matching symbols from anywhere in your project.

Older features include a prettier based formatter for monkeyc, and a monkeyc optimizer that will build/run/export an optimized version of your project.

[edit: My last couple of replies seem to have just disappeared, and the whole conversation seems to be in a jumbled order, so tldr: there's a new test-release at https://github.com/markw65/prettier-extension-monkeyc/releases/tag/v2.0.9 which seems to work for me on linux. I'll do more verification tomorrow, and push a proper update to the vscode store once I'm sure everything is working]

  • It only really matters if it prevents you from running a debug build in the simulator. But there's a lot of data included in debug builds that's not there in release builds.

    All I'm saying is that it's quite possible that 4.2.0 is including more debug info than 4.1.7 in order to improve the debug experience. And what you ultimately care about is how much memory your app uses in release mode. So its better to compare release builds (or at least, if you notice something odd like this, its worth checking the release builds to see whether its a "real" problem, or not).

    [Edit: I just tried building my project with both, and the release builds are identical - both data and code. With a debug build, code size is 11224 for both, and data size is 4522 with 4.2.0, and 4453 with 4.1.7. So like you, I see a small data size increase with 4.2.0 debug builds.]

  • I started to write a python script that generates some constants and functions for every device (from simulator.json). I noticed that constants that are Array or Dictionary are kept in the optimized code even if they are not referenced. Can you remove them?

  • Yes. This is mostly just down to the way the code evolved. I think there's a simple fix - but as I said a few days ago, I want to rewrite how this works - instead of looking at the whole program, and seeing what's not referenced from anywhere, I want to start from the entry points, figure out what's reachable, and then drop everything else.

    If the simple fix doesn't introduce any issues, I'll probably include that in the next release.

  • I think there's another compiler bug in SDK 1.4.7. My original code compiles:

    import Toybox.Lang;
    import Toybox.Graphics;
    import Toybox.WatchUi;
    const DATAFIELD_HASH_2_DICT = {
     2134095 /*260x260@blrt*/ => {
         :label_font => Graphics.FONT_XTINY,
      },
     2131109 /*260x74@lr*/ => {
         :label_font => null,
      },
    } as Dictionary<Number, Dictionary<Symbol, Graphics.FontDefinition?>>;
    (:inline)
    function datafield_label_font(width as Number, height as Number, obscurityFlags as DataField.Obscurity) as Graphics.FontDefinition? {
       var dict = DATAFIELD_HASH_2_DICT[(((width << 9) | height) << 4) | obscurityFlags];
       return dict == null ? null : dict[:label_font] as Graphics.FontDefinition?;
    }
    
    function onUpdate(dc) {
        var labelFont = datafield_label_font(dc.getWidth(), dc.getHeight(), getObscurityFlags());
        var labelFontHeight = labelFont == null ? 0 : dc.getFontHeight(labelFont);
    }
    

    but the generated code doesn't compile for some reason I can't understand:

    function onUpdate(dc) {
        var width = dc.getWidth();
        var height = dc.getHeight();
        obscurityFlags = getObscurityFlags();
        var labelFont;
        {
          zoneColor /*>dict<*/ =
            DATAFIELD_HASH_2_DICT[
              (((width << 9) | height) << pre_4) | obscurityFlags
            ];
          labelFont =
            zoneColor /*>dict<*/ == null
              ? null
              : zoneColor /*>dict<*/[:label_font] as Graphics.FontDefinition?;
        }
        pre_0 = 0;
        var labelFontHeight =
          labelFont == null ? pre_0 : dc.getFontHeight(labelFont);
    }

    There are other lines before this in the function but that shouldn't have effect on this. I get this compiler error:

    ERROR: fr255: bin/optimized/group008-debug/source/source/MyField.mc:17,4: Passing 'PolyType<Null or $.Toybox.Graphics.FontDefinition>' as parameter 1 of poly type 'PolyType<$.Toybox.Graphics.FontDefinition or $.Toybox.Graphics.FontReference or $.Toybox.WatchUi.FontResource>'.

  • but the generated code doesn't compile for some reason I can't understand

    Doesn't make sense to me either. In the code you posted, in the call to dc.getFontHeight, labelFont can't possibly be null, so yes, I think its a bug tracking nullness.

    You can presumably work around it by casting to FontDefinition in the original source. But I'm pretty sure you shouldn't have to.

  • I have something strange:

    when I add this to my code to a global scope (not using it at all):

    var TEST_DICT_ARR as Dictionary<Number, Array> = {} as Dictionary<Number, Array>;
    fr255: no-prettier: code: +14, data: +8 (makes sense)
    fr255: prettier: code: +14, data: +8 (makes sense)
    fenix6: no-prettier: code: +14, data: +8 (makes sense)
    fenix6: prettier: code: +15 (strange), data: +2169 (no typo: two thousand sixtynine)
    I compared the generated outputs, and the only difference is the added variable. 
    > Executing task: "connectiq-sdk-mac-4.1.7-2022-11-21-562b8a195/bin/monkeyc" "-o" "bin/optimized-ExtHRM.prg" "-f" "bin/optimized/debug.jungle" "-y" "garmin_developer_key.der" "-w" "-l" "3" "-d" "fenix6_sim" <
    At first I thought that there is some boundary that when the code size passes it it adds more data, not only what is needed, so I decreased the size of my code by disabling all features in the jungle (adding lots of excludeAnnotations. But even then the same strange thing happens on fenix6.
    I can't find any explanation why adding an empty Dict to fenix6 adds more than 2kB data. Any idea?
    This is the mir without the Dictionary:
    [ @file = "bin/optimized/group005-debug/source/devices/fenix6/label_font.mc"; @line = 3; ]
    import Toybox.Lang;
    [ @file = "bin/optimized/group005-debug/source/devices/fenix6/label_font.mc"; @line = 4; ]
    import Toybox.Graphics;
    [ @file = "bin/optimized/group005-debug/source/devices/fenix6/label_font.mc"; @line = 5; ]
    import Toybox.WatchUi;
    [ @file = "bin/optimized/group005-debug/source/devices/fenix6/label_font.mc"; @line = 3; ]
    <init> {
    }
    
    and with the Dictionary:
    [ @file = "bin/optimized/group005-debug/source/devices/fenix6/label_font.mc"; @line = 3; ]
    import Toybox.Lang;
    [ @file = "bin/optimized/group005-debug/source/devices/fenix6/label_font.mc"; @line = 4; ]
    import Toybox.Graphics;
    [ @file = "bin/optimized/group005-debug/source/devices/fenix6/label_font.mc"; @line = 5; ]
    import Toybox.WatchUi;
    [ @file = "bin/optimized/group005-debug/source/devices/fenix6/label_font.mc"; @line = 6; @position = 4; ]
    var TEST_DICT_ARR as Dictionary<Number, Array>;
    [ @file = "bin/optimized/group005-debug/source/devices/fenix6/label_font.mc"; @line = 3; ]
    <init> {
    [ "bin/optimized/group005-debug/source/devices/fenix6/label_font.mc" 6 4 ]
        %tmp.1 = newd Dictionary<Number, Array> 0;
        putv self :TEST_DICT_ARR %tmp.1;
    }
    
    All the other mir files are identical
    I tested it in another project, and it doesn't happen there, so it is somehow related to my other code in this project... Very strange.
  • feature-request: can you add the device name to the line where you print the code and data size?

  • I tested it in another project, and it doesn't happen there, so it is somehow related to my other code in this project

    Yes - very strange. The .mir doesn't really tell us much - thats basically the result of parsing the source code, before any optimizations or excludeAnnotations are applied. You could try compiling with -g and capture the output, because thats basically the input to the assembler. So you've got all the byte code, and all the data there...

  • feature-request: can you add the device name to the line where you print the code and data size

    sure

  • Holy crap!

    I diffed them and I see lots of strings added. For example:
    > str_MyApp_bin_optimized_group005_debug_source_source_test_Test_mc_1032749526:
    > STRING "MyApp/bin/optimized/group005-debug/source/source/test/Test.mc" ;

    But this and 100 other similar strings are commented out. In fact the whole test/Test.mc file is commented out alltogether (with // at the beginning of every line)

    So this looks like some serious bug in the compiler, but still what I don't understand is why this happens only when I compile with the prettier optimizer, and not when I compile without it.