Under Review
over 1 year ago

Referencing an imported module doesn't run its parent's

If I have a program like:

import Toybox.Application;
import Toybox.System;

class MyApp extends Application.AppBase {

    function initialize() {
        AppBase.initialize();
        System.println(Rez.Strings.AppName);
    }
    ...
}

it prints out a random (well not really) number. It should be a Symbol, but I already filed a bug against that.

But if I import Rez.Strings, it doesn't work:

import Toybox.Application;
import Toybox.System;
import Rez.Strings;

class MyApp extends Application.AppBase {

    function initialize() {
        AppBase.initialize();
        System.println(Strings.AppName);
    }
    ...
}

Now it prints null.

But if I change it to

import Toybox.Application;
import Toybox.System;
import Rez.Strings;

class MyApp extends Application.AppBase {

    function initialize() {
        AppBase.initialize();
        System.println(Strings.AppName);
        var x = Rez;
        System.println(Strings.AppName);
    }
    ...
}

I get null from the first println, and a random number from the second. So reading Rez fixes it.

I think the problem is that the <init> routine for a module runs the first time you mention that module. So if I say Rez.Strings, the init for Rez runs. Similarly, just assigning Rez to a local seems to cause it to run. But going directly for Strings via an import bypasses that, and we get to read uninitialized data (or rather, initialized to null).

My guess is that when getm or getv fetch a module, the runtime checks to see if the module's init has already run, and if not runs it. The bug seems to be that it should first check that any outer module's init has also run.

I've verified the bug with 4.1.5, 4.1.7 and 4.2.0beta2

  • understand what you see is the object number

    The issue here is that calling WatchUi.loadResource(Strings.AppName) would crash because Strings.AppName is null, which is the bug I'm reporting here.

    As I made clear, I'm *expecting* a "random" number. I'm certainly *not* expecting null.

  • when you have

    System.println(Rez.Strings.AppName);

    understand what you see is the object number, which isn't consistent when you run an app more than once.   If you want AppName, you need to do a loadResource() on that object.:

    Such as

    appname=WatchUi.loadResource(Rez.Strings.AppName);

  • The title was supposed to say "doesn't run its parent's <init> method". But I think the angle brackets messed it up...