• I was testing espruino on the Linux command line (while waiting for my boards to be shipped) and ran across this error. Is this a bug, or a feature?

    $ espruino
    Interactive mode.
    Size of JsVar is now 32 bytes
    Size of JsVarRef is now 4 bytes
    Added SIGINT hook
    Added SIGHUP hook
    Added SIGTERM hook
    
     ____                 _ 
    |  __|___ ___ ___ _ _|_|___ ___ 
    |  __|_ -| . |  _| | | |   | . |
    |____|___|  _|_| |___|_|_|_|___|
             |_| espruino.com
     2v04.8 (c) 2019 G.Williams
    
    Espruino is Open Source. Our work is supported
    only by sales of official boards and donations:
    http://espruino.com/Donate
    
    >function foo(x,y) {
    :for (var i = 0; i < arguments.length; i++) {
    :console.log(arguments[i]);
    :}
    :}
    =function (x,y) { ... }
    >foo(1,2)
    1
    2
    =undefined
    >foo(100)
    100
    Uncaught ReferenceError: "1" is not defined
     at line 2 col 24
    console.log(arguments[i]);
                           ^
    in function "foo" called from line 1 col 8
    foo(100)
           ^
    >quit()
    =undefined
    >
    

    The source was cloned from github and compiled on Ubuntu 16.04.6 LTS. I got the same error on an OS X build as well. It was built with the following commands:

    export BOARD=LINUX
    source ./scripts/provision.sh $BOARD
     make clean && RELEASE=1 BOARD=$BOARD make
    

    I saw this post Functions with unknown amount of args. but I didn't get the same results.

  • Fri 2019.08.16

    'Is this a bug, or a feature?'

    Aren't all bugs really 'Undocumented Features'?



    Hello Windy City @jrh from an Espruino neighbor 100 miles N.W. of you. Waukesha, WI home of Les Paul inventor of the solid body electric guitar. There are at least two of us here in the Midwest. Welcome to the world of Espruino!



    Curious about your post, I tested the same on an EspruinoWiFi, and verify the same output.

    >foo( 100 )
    100
    Uncaught ReferenceError: "1" is not defined
    



    But looking under the hood reveals the culprit. I copied your snippet verbatim here for ease of others to copy and follow along as well.

    function foo(x,y) {
      for ( var i=0; i < arguments.length; i++ ) {
        console.log( arguments[i] );
      }
    }
    
    foo( 2, 3 );
    
    foo( 100 );
    



    Q: What is the container arguments?

    When we add this line inside the function and run, we get:

    console.log( typeof( arguments ) );
    
    >foo( 2, 3 )
    object
    

    So we have a container that is an object that we know from experience, massages the values or arguments that are passed to the function parameters.


    Now we go one step further and wonder well how many values does this arguments container actually receive?

    console.log( arguments.length );
    
    foo( 2, 3 );
    2
    



    So now we have our test snippet:

    function foo(x,y) {
      console.log( typeof( arguments ) );
      console.log( arguments.length );
    
      for ( var i=0; i < arguments.length; i++ ) {
        console.log( arguments[i] );
      }
    }
    

    Now lets proceed to the actual perceived error:

    Uncaught ReferenceError: "1" is not defined
    

    This is informing us that as we are dealing with an object that holds an array of values that when referenced with an 'i' indexer arguments[i] we may reveal what value is in each array location. The above error indicates that element 1 doesn't exist.

    Let's now take a peek at the function call:

    foo(100);
    



    And for the BIG SURPRISE!, I actually thought I had the obvious answer, until I did a bit more testing. Had to wrap the function contents in a try/catch to allow processing to continue and give us a bit more detail.

    function foo(x,y) {
      console.log( typeof( arguments ) );
      console.log( arguments.length );
    
      try {
    
      for ( var i=0; i < arguments.length; i++ ) {
        console.log( arguments[i] );
      }
    
      } catch(e) {
        console.log( "ERROR: " + e.message );
      }
    }
    

    Running our test produces the same error.

    >foo(100)
    object
    2
    100
    ERROR: "1" is not defined
    

    and then on to two additional lines for the Coup de Gras:

    function foo(x,y) {
      console.log( typeof( arguments ) );
      console.log( arguments.length );
    
      try {
      
      console.log( arguments[0] );
      console.log( arguments[1] );
    
    //  for ( var i=0; i < arguments.length; i++ ) {
    //    console.log( arguments[i] );
    //  }
    
      } catch(e) {
        console.log( "ERROR: " + e.message );
      }
    }
    

    provides some additional information, but we still need a bit more, however

    >foo(100)
    object
    2
    100
    ERROR: "1" is not defined
    



    Final test is to modify what is passed as the parameter list. We'll change the function definition to have just a single 'x' parameter foo( x )

    function foo(x) {
      console.log( typeof( arguments ) );
      console.log( arguments.length );
    
      try {
      
      console.log( arguments[0] );
    //  console.log( arguments[1] );
    
      for ( var i=0; i < arguments.length; i++ ) {
        console.log( arguments[i] );
      }
    
      } catch(e) {
        console.log( "ERROR: " + e.message );
      }
    }
    

    the result is as expected in our deign requirement expecting one argument be passed

    >foo(100)
    object
    1
    100
    100
    


    If you have been following along, I believe you now have the answer to the question is this a bug?



    Further clarity may be gained from the docs:

    https://www.espruino.com/Reference#l__glĀ­obal_arguments

  • Yes it's a bug. arguments.length is set to the number of arguments if the number of provided arguments is higher (and/or equal) to the number specified arguments in the function definition. But it is not set to the number of arguments if the number of provided arguments is smaller.

    As workaround define the function without arguments, arguments.lengthwill be always correct.

    function foo(x,y) {
      console.log("arguments.length=" + arguments.length);
      //console.log("x=" + x);
      //console.log("y=" + y);
      //for ( var i=0; i < arguments.length; i++ ) {
      //  console.log("typeof(arguments["+ i + "])=" + typeof(arguments[i]));
      //  console.log("arguments["+ i + "]=" + arguments[i]);
      //}
    }
    function bar() {
        console.log("arguments.length=" + arguments.length);
    }
    foo();
    foo(1);
    foo(1,2);
    foo(1,2,3);
    bar();
    bar(1);
    bar(1,2);
    bar(1,2,3);
    
About

Avatar for jrh @jrh started