Skip to content

ERROR: indexing array with string in Promise.serial() #44

@gjanvier

Description

@gjanvier

Promise.serial() makes my imp04 device crash.

I found a workaround by reimplementing this function with a tiny variation, but the fact that it solves the issue makes no sense for me. I don't know what I am talking about, but I suspect a bug in the squirel VM itself. So please consider my test case.

I tried to simplify my app, so this sample may seem to be too complex for the features it implements.

RUN on imp04, device or agent.

#require "Promise.lib.nut:4.0.0"

// Here we will test several variations of Promise.serial

function Promise_serial(promises) {
    // static method
    return Promise.serial(promises);
}

function Promise_serial1(promises) {
    // Copy/paste of Promise.serial
    local i = 0;
    return Promise.loop(
        @() i < promises.len(),
        function () {
            return "function" == type(promises[i]) // you'll see that this i integer will become a string??
                 ? promises[i++]()
                 : promises[i++];
        }
    )
}

function Promise_serial2(promises) {
    // Just a tiny change in Promise.serial
    local i = 0;
    return Promise.loop(
        @() i < promises.len(),
        function () {
            // I don't know why but I had to change the order here
            local res = promises[i++];
            if ("function" == type(res)) {
                res = res();
            }
            return res;
        }
    )
}

// My simplified app
// I removed everything that is not required to reproduce the bug

/**
 * Simulates an asynchronous command that is sent to a device throuh a serial interface
 * and its response.
 */
function executeCommand(cmd) {
    server.log("--> " + cmd);
    return Promise(function(resolve, reject) {
        imp.wakeup(0, function() {
            server.log("<-- " + cmd);
            resolve(true);
        });
    });
}


currentCommandPromise <- null;

/**
 * Wrapper that make sure that only one command is running at a time.
 */
function commandWrapper(callback) {
    if (null == currentCommandPromise) {
        currentCommandPromise = callback();
    }
    else {
        currentCommandPromise = currentCommandPromise.then(
            function(value) { // onFulfilled
                return callback();
            },
            function(reason) { // onError
                logger.error("Failed callback: " + reason);
                return callback();
            }
        );
    }

    return currentCommandPromise;
}

/**
 * Callback executed when the device starts
 */
function onStartCallback() {
    local promises = [
        function() {
            return executeCommand("onStartCallback");
        },
        // We would expect more commands here
    ];

    return commandWrapper(function() {
        return Promise.serial(promises);
    });
}


// My tests with the Promise.serial variation


function test() {
    server.log("Run test");
    
    local promises = [
        function() {
            return onStartCallback();
        },
        // We would expect more commands here
    ];

    Promise_serial(promises);
}

function test1() {
    server.log("Run test1");
    
    local promises = [
        function() {
            return onStartCallback();
        },
        // We would expect more commands here
    ];

    Promise_serial1(promises);
}

function test2() {
    server.log("Run test2");
    
    local promises = [
        function() {
            return onStartCallback();
        },
        // We would expect more commands here
    ];

    Promise_serial2(promises);
}


// main
// Pick the test you want
test(); // crash with ERROR: indexing array with string
//test1(); // crash with ERROR: indexing array with string
//test2(); // this test works fine

If you run test() or test1() which runs the same code:

2021-03-22T21:57:01.360 +00:00 | [Agent] | Run test
2021-03-22T21:57:01.374 +00:00 | [Agent] | ERROR: indexing array with string
2021-03-22T21:57:01.374 +00:00 | [Agent] | ERROR:   in commandWrapper agent_code:64
2021-03-22T21:57:01.374 +00:00 | [Agent] | ERROR:   from unknown electricimp#promise.lib.nut#4.0.0:251
2021-03-22T21:57:01.374 +00:00 | [Agent] | ERROR:   from unknown electricimp#promise.lib.nut#4.0.0:219

But if you run test2() it is OK

2021-03-22T21:57:25.095 +00:00 | [Agent] | Run test2
2021-03-22T21:57:25.133 +00:00 | [Agent] | --> onStartCallback
2021-03-22T21:57:25.153 +00:00 | [Agent] | <-- onStartCallback

I let you play with this test2() and try to understand what makes the difference. From my understanding, it is the position of the i++. There is no reason for it, but in the crashing tests, this integer suddenly becomes a string!?!

I've spend quite a lot of time to investigate, don't hesitate to point out any stupid error I made, but please enlighten me ;-)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions