ConnectIQ 2.3.1: method(:myMethod).invoke()= could not find symbol method (sometimes)

A Descriptive Title
method(:testMethod) gives an error in certain contexts. Used to work fine on ConnectIQ 2.2.6

The Environment:
- OS X and Windows
- Eclipse Neon.2 (4.6.2) and Mars (4.5.2)
- ConnectIQ 2.3.1

A detailed description of the issue
- A function testMethod() is created out of a class and without explicitly defining a module.
- It can be called from inside a class and from other functions at the same level
- It can also be called from inside a class using method(:testMethod).invoke()
- It can also be called from other functions at the same level if method(:testMethod) is passed as a parameter and then .invoke() is called on this parameter.
- But if you use method(:testMethod) in a function on that same level, you get a runtime error: "Could not find symbol method"

It worked fine on ConnectIQ 2.2.6, both on simulator and real devices. Is it a bug or not allowed anymore?


Steps to reproduce the issue

using Toybox.WatchUi as Ui;
using Toybox.System as Sys;

class TestFooView extends Ui.View {

function onUpdate(dc) {
// Call the parent onUpdate function to redraw the layout
View.onUpdate(dc);

Sys.println("Calling testMethod() from TestFooView class.");
testMethod();

Sys.println("Calling method(:testMethod).invoke() from TestFooView class.");
method(:testMethod).invoke();

Sys.println("Calling callTestMethodFromTheSameLevel() from TestFooView class.");
callTestMethodFromTheSameLevel();

Sys.println("Calling receiveMethodObjAndInvoke(method(:testMethod)) from TestFooView class.");
receiveMethodObjAndInvoke(method(:testMethod));

Sys.println("Calling createMethodObjAndInvoke() from TestFooView class.");
createMethodObjAndInvoke();
}

function onHide() {}
function initialize() {View.initialize();}
function onLayout(dc) {setLayout(Rez.Layouts.MainLayout(dc));}
function onShow() {}
}

function callTestMethodFromTheSameLevel(){
Sys.println(" callTestMethodFromTheSameLevel() calls testMethod()");
testMethod();
}

function receiveMethodObjAndInvoke(aMethod){
Sys.println(" receiveMethodObjAndInvoke(aMethod) calls aMethod.invoke()");
aMethod.invoke();
}

function createMethodObjAndInvoke(){
Sys.println(" createMethodObjAndInvoke() calls method(:testMethod).invoke()");
method(:testMethod).invoke(); // <------------------------------------------ HERE IS THE PROBLEM!
}

function testMethod(){
Sys.println("-> OK! testMethod() responded.");
}


Output CIQ 2.3.1:
Calling testMethod() from TestFooView class.
-> OK! testMethod() responded.
Calling method(:testMethod).invoke() from TestFooView class.
-> OK! testMethod() responded.
Calling callTestMethodFromTheSameLevel() from TestFooView class.
callTestMethodFromTheSameLevel() calls testMethod()
-> OK! testMethod() responded.
Calling receiveMethodObjAndInvoke(method(:testMethod)) from TestFooView class.
receiveMethodObjAndInvoke(aMethod) calls aMethod.invoke()
-> OK! testMethod() responded.
Calling createMethodObjAndInvoke() from TestFooView class.
createMethodObjAndInvoke() calls method(:testMethod).invoke()
Could not find symbol method.Failed invoking <symbol>Symbol Not Found Error
in createMethodObjAndInvoke (C:\Users\rrezende\workspace\TestFoo\source\TestFooView.mc:44)
in onUpdate (C:\Users\rrezende\workspace\TestFoo\source\TestFooView.mc:23)


Output CIQ 2.2.6:
Calling testMethod() from TestFooView class.
-> OK! testMethod() responded.
Calling method(:testMethod).invoke() from TestFooView class.
-> OK! testMethod() responded.
Calling callTestMethodFromTheSameLevel() from TestFooView class.
callTestMethodFromTheSameLevel() calls testMethod()
-> OK! testMethod() responded.
Calling receiveMethodObjAndInvoke(method(:testMethod)) from TestFooView class.
receiveMethodObjAndInvoke(aMethod) calls aMethod.invoke()
-> OK! testMethod() responded.
Calling createMethodObjAndInvoke() from TestFooView class.
createMethodObjAndInvoke() calls method(:testMethod).invoke()
-> OK! testMethod() responded.
  • Thanks for the detailed report. We'll get this investigated!
  • Thanks for the detailed report. We'll get this investigated!


    What is the status of this? I migrated 6/26 and i must revert to prior version.
  • Former Member
    Former Member
    Hey,

    We've got the ticket prioritized, but there is no work on the fix yet. I will see if I can get a bump in priority here.

    Thanks,
    -Coleman
  • I have the same issue, on ConnectIQ 2.3.1, Vivoactive HR :-( :-( -- my own app was working before but after the firmware update is not working anymore :-(

    My app source is here, issue on line 107: https://github.com/BodyFatControl/garmin_watchface/blob/master/source/DFC_garmin_watchfaceView.mc

    I wish I there is a workaround or a way to go back to the previous firmware version!!
  • I wish I there is a workaround or a way to go back to the previous firmware version!!

    You can work around the problem by building the Lang.Method object like this...

    function createMethodObjAndInvoke(){
    Sys.println(" createMethodObjAndInvoke() calls method(:testMethod).invoke()");
    var aMethod = new Lang.Method($, :testMethod);
    aMethod.invoke();
    }

  • This particular problem being reported isn't with the invoke() call itself, it is an issue with the lookup for method().

    The function named method() is a member of the Object class, which is the base-most of every class or module in the MonkeyC language. When you write method(:testMethod) inside TestFooView.onUpdate(), you are implicitly calling TestFooView.method(:testMethod). The method() implementation appears to just create a Lang.Method object with self and the provided symbol as parameters. When you invoke the resulting method object, it looks up the given symbol like it would for a normal function (by looking in the surrounding scopes) and then invokes it as if you had called self.testMethod()..

    You may think to yourself, "you're saying that the call method(:testMethod).invoke() call in TestFooView.onUpdate() essentially calls self.testMethod()?" Yes, that is exactly what I'm saying, and you can verify it yourself...

    Sys.println("");

    var aMethod;

    aMethod = method(:testMethod);
    aMethod.invoke();

    aMethod = self.method(:testMethod);
    aMethod.invoke();

    aMethod = new Lang.Method(self, :testMethod);
    aMethod.invoke();

    self.testMethod();

    testMethod();


    That code produces the following output...

    -> OK! testMethod() responded.
    -> OK! testMethod() responded.
    -> OK! testMethod() responded.
    -> OK! testMethod() responded.
    -> OK! testMethod() responded.


    Most of this doesn't make any sense. The self object does not have a method named testMethod, so I would expect calls to self.testMethod() to throw a Symbol Not Found exception regardless of whether or not self was explicitly specified. I don't think anyone would expect an call to self.testMethod() to invoke $.testMethod(), but that is exactly what what happens.

    I think it would be nice for self.method(:testMethod) to throw the Symbol Not Found exception since self does not have a method by that name. That said, I understand that adding that check would add some overhead. The advantage of adding it is that the user would get useful error messages closer to the source of the actual problem.
  • You can work around the problem by building the Lang.Method object like this...

    function createMethodObjAndInvoke(){
    Sys.println(" createMethodObjAndInvoke() calls method(:testMethod).invoke()");
    var aMethod = new Lang.Method($, :testMethod);
    aMethod.invoke();
    }



    Travis, would you be kind enough to make the needed adaptation for my code?? because I don't have enough knowledge to use the information that you shared...

    My app source is here, issue on line 107: https://github.com/BodyFatControl/ga...tchfaceView.mc
  • Could you just post the few lines of code involved here? That would make it easier to post what you have and what to do to fix it here.
  • Travis, would you be kind enough to make the needed adaptation for my code??

    There are really multiple solutions. The more correct solution (in my mind) is to get rid of the global functions onPhone and onSensorHR by making them methods of a class and then registering them with the system from that class. You should be able to move all of the methods to your app class like this...

    //
    // Note that I'm using C99 digraphs <% and %> to represent curly braces { and } below.
    //
    // This is only done so that I can post code to the forums. I cannot post code if I don't do this.
    // You will have to undo it if you actually use the code.

    class DFC extends App.AppBase {

    function initialize() <%
    AppBase.initialize();
    %>

    function onStart(state) <%
    Comm.registerForPhoneAppMessages(self.method(:onPhone));
    %>

    function onStop(state) <%
    Comm.registerForPhoneAppMessages(null);
    %>

    function getInitialView() <%
    return [ new DFC_garmin_watchappView(), new BaseInputDelegate()];
    %>

    //
    // the guts of the following functions should be moved from DFC_garmin_watchappView.mc to here
    //

    function onPhone(msg) <%
    %>

    function onSensorHR(info) <%
    %>

    function enableHRSensor() <%
    %>

    function disableHRSensor() <%
    %>
    }


    The other option would be to not use the method() function when binding global methods. You need to replace these lines...

    107 Sensor.enableSensorEvents(method(:onSensorHR));

    // ...

    196 phoneMethod = method(:onPhone);
    197 Comm.registerForPhoneAppMessages(phoneMethod);


    ... with these ...

    107 Sensor.enableSensorEvents(new Lang.method($, :onSensorHR));

    // ...

    196 Comm.registerForPhoneAppMessages(new Lang.Method($, :onPhone));


    Travis
  • Thank you jim_m_58 and travis.vitek!!

    I am short of time and just tried Travis quick solution but I always get this error:

    ERROR: Symbol Not Found Error
    DETAILS: Failed invoking <symbol>
    STORE_ID: 00000000000000000000000000000000
    CALLSTACK:
    @PC = 0x100003bc
    @PC = 0x10000175
    @PC = 0x10001ed0
    @PC = 0x30001c0d
    @PC = 0x30001e38

    The updated source code is here: https://github.com/BodyFatControl/garmin_watchface/blob/v2.x-Precise/source/BodyFatControl_garmin_watchappView.mc