Oh, that is a good one. That took me all morning to figure out. (It was a worry, because wrong types out of nowhere can be caused by stack imbalances, and stack imbalances can lead to sandbox escapes.)
But there is no bug here, just an extremely odd Squirrel feature. The error isn’t being raised from the lookup of “i++” in “promises”, it’s being raised when “currentCommandPromise” is being looked-up inside the function commandWrapper. “But what lookup? in what array?”, I hear you ask.
So. The Squirrel feature in question here is “automatic context-object setting”. In Squirrel, all function calls have an implicit context-object, which is usually the global table for freestanding functions, and the class instance for class-method functions. The context-object is available inside the function itself under the name “this”, and is usually implicitly passed on to any further functions called, but it can be overridden (made explicit) using bindenv().
If “p” is an instance of class Promise, and you write a method call such as “p.fail(…)”, then the “fail” function really needs to have “p” as its context-object in order to access member variables and functions of that particular Promise. So when you extract a function from an object and then immediately call that function, Squirrel automatically uses the very object it was extracted from, as that function call’s context-object.
So far so good. The extremely odd bit, is that Squirrel also does this for extracting a function from an array and then immediately calling it – the array is set as the context-object:
local promises = [
function() {
server.log("type(this)="+type(this));
},
];
promises[0]();
This is unfortunate, because arrays do a very poor job of being context-objects – not least because non-local variables are first looked up in the current context-object by name, before the root table is checked!
So the short answer is, your Promise_serial1 and Promise_serial2 are not as equivalent as they look, because:
promises[i++]();
triggers automatic context-object setting, whereas:
local res = promises[i++];
res();
does not. Promise_serial and Promise_serial1 both end up calling onStartCallback with the “promises” array as its context-object; onStartCallback fortuitously accesses no non-local variables before it calls on to commandWrapper with the same context-object. But commandWrapper does access the non-local variable currentCommandPromise. And that’s where your error came from.
Our own Promise.serial, in Promise library 4.0.0, turns out to be a bit of a bear-trap due to its use of the former construct. I’ll raise an internal bug.