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]

  • Right, the problem here is that I had to hardcode the type of Rez.Strings.*, because there's nothing in the sdk to tell me what it is (in hindsight, I suppose I could have looked at the signature for Application.loadResource, and picked out the type of its argument; but that didn't seem like something that was going to change).

    On the other hand it gets the signature for Application.loadResource from <sdk>/bin/api.mir, and under 7.x that says it expects a ResourceId. Hence the "error".

    I just need to make it give the correct type to Rez.Strings.* based on what the sdk says.

  •             (:no_inline)
                public function computeImpl(info as Activity.Info) as ValueType {
                    return null;
                }
    
                // Calculate the data to display in the field here
                public function compute(info as Activity.Info) as Void {
                    var value = computeImpl(info);
                    var currentValue = self.value;
                    if (value != null ? !value.equals(currentValue) : currentValue != null) {
                        log("compute3: " + value);
                        self.value = value;
                        WatchUi.requestUpdate();
                    }
                }
    

    So this code is part of what in other language would be called an abstract class. computeImpl is what should be overridden in the child class. Obviously the "var value = computeImpl(info)" shouldn't be eliminated, but this is how it is optimized:

          // Calculate the data to display in the field here
          function compute(info as Activity.Info) as Void {
            if (self.value != null) {
              log("compute3: null");
              self.value = null;
              WatchUi.requestUpdate();
            }
          }
    

    Now let me add 2 pieces of information:
    1. IMHO computeImpl shouldn't be eliminated even if it's:

                public function computeImpl(info as Activity.Info) as ValueType {
                    return null;
                }
    

    just because it's public!

    2. it certainly shouldn't be inlined when (:no_inline) annotation is present.

  • It depends. If there's no overriding implementation then what's going on here is fine. And I definitely have checks for that - although if you're saying that you do have another class that overrides it, and this is happening, then that's obviously a bug [Ok, I just checked, and yes, it does happen regardless - so definitely a bug].

    As far as the no_inline goes, I don't think that's actually a thing. The optimizer doesn't check for no_inline anywhere. I think if you've been using it, it's just documentation. Or maybe I promised it at some time and forgot to implement it :-)

  • But I do have a child class that overrides computeImpl. That's the reason I know about the bug, because I don't see my data on the screen

  • That's why I wrote:

    Ok, I just checked, and yes, it does happen regardless - so definitely a bug

    Anyway, I found the bug. As I said, there was already a test for no overriding functions, but it wasn't being checked everywhere. There was one path into the inliner that didn't check it. So it's basically a one line fix.

    I'm going to write some more tests, and then see what I can do for the stack overflow issue.

  • New version v2.0.75 [edit: 2.0.76] is out.

    • Fixes the type analysis bug reporting an error for Application.loadResource(Rez.Strings.Foo) in sdk-7.x
    • Fixes an incorrect inlining issue when the callee might be overridden
    • Fixes a post build optimizer issue that could cause stack overflows for large, initialized arrays
  • Something isn't right. 2.0.75 was released 2 days ago, that's my current one.

    update: OK I see now 2.0.76, and it indeed works.

  • Is there a way I can know if the build is with or without Prettier Optimizer? My goal is something like:

    (:when_with_prettier)
    const HUGE_ARRAY = [... 100 items];

    (:when_without_prettier)
    const HUGE_ARRAY = [... 30 items];

    A bit similar to (:debug) vs (:release)

  • This one was because the post build optimizer reduces the size of array initializers by pushing array/index pairs onto the stack in a loop, and then doing the assignments one at a time in reverse order, cleaning one array/index pair per assignment. This saves 7 bytes per element, with an overhead of 22 bytes for the loop - but it temporarily pushes 2 items for each array element onto the stack.

    It looks like data fields only get 128 stack slots - and obviously a few are already in use - so it started to run into problems with around 60 elements in the array. Watch Apps have much bigger stacks, but for now I'm limiting the number of elements to 50 less however much the optimizer knows is already on the stack, regardless of the type of app being built.

    It would also be possible to push the initializers onto the stack first, and then do the assignments in a loop. That would only require one stack slot per array element, but would make the loop more complex (I can't see a way to do it without introducing a couple of new local variables). I might look into that.

  • I haven't check this in SDK7, but in 6 now I get this underlined with red:

    Application.loadResource(Rez.JsonData.label_data_1);
    Argument 1 to $.Toybox.Application.loadResource expected to be Symbol but got Any [pmc-build]
    Argument 1 to $.Toybox.Application.loadResource expected to be Symbol but got Any [pmc-analysis]